Skip to content

Conversation

@BlindsidedGames
Copy link
Contributor

@BlindsidedGames BlindsidedGames commented Jan 15, 2026

Summary

Fixes #555 - Infinite compilation/refresh loop when using refresh_unity with compile="request" and wait_for_ready=true in Unity 6.

Root Cause

In Unity 6, the WaitForUnityReadyAsync method's use of EditorApplication.update += Tick to poll for compilation completion causes infinite domain reload loops. The async Task + EditorApplication.update callback combination doesn't survive domain reloads properly, causing the editor to repeatedly recompile until timeout.

Fix

Skip WaitForUnityReadyAsync when compileRequested is true. Return immediately and let the client poll editor_state resource instead.

This is a minimal, targeted fix that:

  • Only affects the specific case of compile="request" + wait_for_ready=true
  • Maintains backward compatibility for all other refresh scenarios
  • Follows the existing pattern of returning resulting_state="compiling" with a hint to poll

Testing

Environment:

  • Unity 6000.3.0f1 (Unity 6)
  • MCP for Unity v9.0.7
  • Claude Code VS Code Extension

Before fix: refresh_unity with compile="request" and wait_for_ready=true causes ~3 compilation loops before timing out.

After fix: Single compilation, immediate return with resulting_state="compiling".

Notes

🤖 Generated with Claude Code

Summary by Sourcery

Prevent infinite compilation loops in Unity 6 when using refresh commands that request compilation and wait for readiness.

Bug Fixes:

  • Avoid infinite compilation/refresh loops in Unity 6 by skipping the wait-for-ready flow when a compile has been explicitly requested and letting clients poll editor state instead.

Enhancements:

  • Gate the wait-for-ready behavior on a new conditional flag that preserves existing behavior on earlier Unity versions while applying the fix only on Unity 6+.

Summary by CodeRabbit

  • Bug Fixes
    • Improved editor refresh logic to avoid unnecessary waiting during compilation or domain reloads, improving responsiveness.
    • Updated post-refresh messaging so guidance accurately reflects whether the editor actually waited or is polling, including behavior adjustments for newer Unity versions.

✏️ Tip: You can customize this high-level summary in your review settings.

…_ready

Skip WaitForUnityReadyAsync when compileRequested is true. The
EditorApplication.update polling doesn't survive domain reloads
properly in Unity 6, causing infinite compilation loops.

When compilation is requested, return immediately and let the
client poll editor_state resource instead.

Fixes CoplayDev#557

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 15, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts the Unity editor refresh command handling so that in Unity 6+ an explicit compile request with wait_for_ready=true no longer awaits WaitForUnityReadyAsync, preventing infinite compilation loops and aligning the hint messaging with the new behavior.

Sequence diagram for refresh_unity with compile=request and wait_for_ready in Unity 6+

sequenceDiagram
    actor Client
    participant MCPServer
    participant RefreshUnity_HandleCommand as RefreshUnity_HandleCommand
    participant WaitForUnityReadyAsync as WaitForUnityReadyAsync
    participant EditorState as editor_state_resource

    Client->>MCPServer: send refresh_unity(compile=request, wait_for_ready=true)
    MCPServer->>RefreshUnity_HandleCommand: HandleCommand(params)

    activate RefreshUnity_HandleCommand
    RefreshUnity_HandleCommand-->>RefreshUnity_HandleCommand: perform asset refresh
    RefreshUnity_HandleCommand-->>RefreshUnity_HandleCommand: set compileRequested = true

    Note over RefreshUnity_HandleCommand: Unity 6+: shouldWaitForReady = waitForReady && !compileRequested

    RefreshUnity_HandleCommand-->>RefreshUnity_HandleCommand: shouldWaitForReady = false
    RefreshUnity_HandleCommand-->>MCPServer: return { resulting_state = compiling, hint = "If Unity enters compilation/domain reload, poll editor_state until ready_for_tools is true." }
    deactivate RefreshUnity_HandleCommand

    MCPServer-->>Client: response(resulting_state=compiling)

    loop poll until ready
        Client->>MCPServer: request editor_state
        MCPServer->>EditorState: read editor_ready status
        EditorState-->>MCPServer: ready_for_tools flag
        MCPServer-->>Client: editor_state(ready_for_tools)
    end

    Client-->>Client: proceed when ready_for_tools is true
Loading

Flow diagram for shouldWaitForReady logic in RefreshUnity.HandleCommand

flowchart TD
    A[Start HandleCommand after refresh/compile setup] --> B{Unity version >= 6000.0?}
    B -- Yes --> C[compute shouldWaitForReady = waitForReady && !compileRequested]
    B -- No --> D[compute shouldWaitForReady = waitForReady]

    C --> E{shouldWaitForReady?}
    D --> E

    E -- Yes --> F[await WaitForUnityReadyAsync]
    F --> G[set resultingState = ready]
    G --> H[set hint = Unity refresh completed; editor should be ready.]
    H --> I[return response]

    E -- No --> J[set resultingState = compiling or current state]
    J --> K[set hint = If Unity enters compilation/domain reload, poll editor_state until ready_for_tools is true.]
    K --> I[return response]

    I --> L[End HandleCommand]
Loading

File-Level Changes

Change Details Files
Guard the wait_for_ready flow behind a Unity 6+ compileRequested check to avoid using WaitForUnityReadyAsync when compilation was explicitly requested, and propagate this logic to the response hint.
  • Introduce a shouldWaitForReady boolean that, on Unity 6+, is true only when waitForReady is requested and compileRequested is false, while preserving the original behavior on earlier Unity versions.
  • Use shouldWaitForReady instead of the raw waitForReady flag to decide whether to invoke WaitForUnityReadyAsync.
  • Use shouldWaitForReady instead of waitForReady when setting the hint message in the response payload so client messaging reflects the new wait behavior.
MCPForUnity/Editor/Tools/RefreshUnity.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#557 Update package dependencies or assembly references so that System.Collections.Immutable version conflicts between Unity 6.3, the Unity AI Assistant package, and MCP (CodeAnalysis dependency) are resolved without manual installation of a specific DLL. The PR only changes the RefreshUnity.cs logic to avoid an infinite compilation loop when using wait_for_ready in Unity 6; it does not modify any package dependencies, assembly references, or DLL versions related to System.Collections.Immutable.
#557 Provide configuration or documentation changes that remove the need for manually installing System.Collections.Immutable v9.0.0 in Assets/Plugins to resolve the DLL reference mismatch. The PR does not include any documentation or configuration changes regarding System.Collections.Immutable or DLL placement; it solely alters runtime editor behavior for refresh/compilation flow.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a Unity-6+ conditional gate to the RefreshUnity wait logic so the code only waits for Unity readiness when waitForReady is true and no script compilation was requested; final hint text now reflects whether the wait actually occurred and comments explain the Unity 6 domain reload rationale.

Changes

Cohort / File(s) Summary
Unity Readiness Wait Logic
MCPForUnity/Editor/Tools/RefreshUnity.cs
Gate waiting with shouldWaitForReady = waitForReady && !compileRequested under UNITY_6000_0_OR_NEWER; run WaitForUnityReadyAsync only when shouldWaitForReady; use shouldWaitForReady to select final hint message. (+12/-2 lines)

Sequence Diagram(s)

(omitted — change is a localized control-flow tweak and does not introduce a multi-component sequential feature)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

Poem

🐇 I nudge the loop, then skip a trot,

If compiles are queued, I leave the spot.
With careful hops and comments neat,
I whisper "wait" when waits are meet.
A tiny rabbit guard — quick, light, and fleet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: preventing an infinite compilation loop in Unity 6 when using wait_for_ready, which matches the core fix in the changeset.
Linked Issues check ✅ Passed The PR addresses the Unity 6 runtime/IPC instability described in #557 by implementing a fix to prevent infinite compilation loops when compile='request' and wait_for_ready=true.
Out of Scope Changes check ✅ Passed All changes are scoped to the compilation loop fix: Unity 6+ gate, shouldWaitForReady logic, and response hint adjustment. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a160cb2 and efcf03b.

📒 Files selected for processing (1)
  • MCPForUnity/Editor/Tools/RefreshUnity.cs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-29T15:23:11.613Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:11.613Z
Learning: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows. This means during reviews, verify that tests cover UI initialization paths and that code avoids repetitive null-check boilerplate in Editor Windows. If a UI element can be null, ensure there is a well-tested fallback or that its initialization is guaranteed by design, rather than sprinkling null checks throughout editor code.

Applied to files:

  • MCPForUnity/Editor/Tools/RefreshUnity.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Sourcery review
  • GitHub Check: Sourcery review
  • GitHub Check: Sourcery review
🔇 Additional comments (3)
MCPForUnity/Editor/Tools/RefreshUnity.cs (3)

93-102: LGTM! Well-documented Unity 6+ workaround.

The conditional compilation approach correctly isolates the fix to Unity 6+ while preserving original behavior for earlier versions. The comment clearly explains the domain reload issue causing infinite loops.


103-124: Correct conditional gating of the wait logic.

The wait block now properly uses shouldWaitForReady, ensuring the problematic WaitForUnityReadyAsync path is bypassed on Unity 6+ when compilation is requested.


130-138: Hint text now correctly reflects actual behavior.

This addresses the previous review feedback about misleading hints. When shouldWaitForReady is false (e.g., compile requested on Unity 6+), the response now correctly advises clients to poll editor_state rather than falsely claiming the editor is ready.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83ddd69b21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 97 to 98
if (waitForReady && !compileRequested)
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid claiming readiness when wait_for_ready is skipped

When compile="request" and wait_for_ready=true, this new guard skips WaitForUnityReadyAsync, but the response still sets the hint based on waitForReady alone. That means the tool will return immediately yet still claim “Unity refresh completed; editor should be ready,” which is false while compilation is still running. Clients that treat wait_for_ready (or the hint) as a readiness guarantee can proceed too early and hit tool failures. Consider basing the hint on waitForReady && !compileRequested (or otherwise signaling that polling is still required) to keep the response consistent with the new behavior.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
MCPForUnity/Editor/Tools/RefreshUnity.cs (1)

129-131: Hint message is inconsistent when compile was requested with wait_for_ready=true.

When waitForReady=true but compileRequested=true, we now skip waiting (per the fix), yet the hint still says "Unity refresh completed; editor should be ready." This is misleading since we didn't actually wait.

Proposed fix
-            return new SuccessResponse("Refresh requested.", new
+            bool actuallyWaited = waitForReady && !compileRequested;
+            return new SuccessResponse("Refresh requested.", new
             {
                 refresh_triggered = refreshTriggered,
                 compile_requested = compileRequested,
                 resulting_state = resultingState,
-                hint = waitForReady
+                hint = actuallyWaited
                     ? "Unity refresh completed; editor should be ready."
                     : "If Unity enters compilation/domain reload, poll editor_state until ready_for_tools is true."
             });
🧹 Nitpick comments (1)
MCPForUnity/Editor/Tools/RefreshUnity.cs (1)

101-103: allowNudge parameter is now always true in this context.

Since we only enter this block when !compileRequested is true, the expression allowNudge: !compileRequested will always evaluate to true. This is harmless but slightly redundant. Consider simplifying to allowNudge: true for clarity, or leave as-is if you anticipate the condition changing.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6fa293 and 83ddd69.

📒 Files selected for processing (1)
  • MCPForUnity/Editor/Tools/RefreshUnity.cs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-29T15:23:11.613Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:11.613Z
Learning: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows. This means during reviews, verify that tests cover UI initialization paths and that code avoids repetitive null-check boilerplate in Editor Windows. If a UI element can be null, ensure there is a well-tested fallback or that its initialization is guaranteed by design, rather than sprinkling null checks throughout editor code.

Applied to files:

  • MCPForUnity/Editor/Tools/RefreshUnity.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (1)
MCPForUnity/Editor/Tools/RefreshUnity.cs (1)

93-97: Well-documented fix for Unity 6 domain reload issue.

The approach of skipping WaitForUnityReadyAsync when compilation is requested is sound. The comments clearly explain the root cause (EditorApplication.update callbacks not surviving domain reloads) and the rationale for the fix. Returning immediately with resulting_state="compiling" and letting the client poll is a pragmatic solution that maintains backward compatibility.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@BlindsidedGames BlindsidedGames marked this pull request as draft January 15, 2026 05:44
Address code review feedback: when compile was requested and we skip
WaitForUnityReadyAsync, the hint should correctly indicate that the
client needs to poll editor_state, not claim the editor is ready.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@BlindsidedGames BlindsidedGames marked this pull request as ready for review January 15, 2026 05:48
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Since the behavior change is specifically to address a Unity 6 issue, consider gating the compileRequested + wait_for_ready bypass behind a Unity version check (e.g., preprocessor symbol or runtime version check) so earlier Unity versions retain the previous behavior unless they exhibit the same problem.
  • The actuallyWaited variable name could be clearer as something like shouldWaitForReady or waitThisCall, since in this context it represents the decision to perform the wait rather than a result determined after the fact.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since the behavior change is specifically to address a Unity 6 issue, consider gating the `compileRequested` + `wait_for_ready` bypass behind a Unity version check (e.g., preprocessor symbol or runtime version check) so earlier Unity versions retain the previous behavior unless they exhibit the same problem.
- The `actuallyWaited` variable name could be clearer as something like `shouldWaitForReady` or `waitThisCall`, since in this context it represents the decision to perform the wait rather than a result determined after the fact.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@BlindsidedGames BlindsidedGames marked this pull request as draft January 15, 2026 05:49
Address code review feedback:
- Use UNITY_6000_0_OR_NEWER preprocessor directive to only apply the
  compilation wait bypass on Unity 6+, preserving original behavior
  for earlier versions
- Rename actuallyWaited to shouldWaitForReady for clarity, as it
  represents the decision to wait rather than a post-hoc result

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@BlindsidedGames BlindsidedGames marked this pull request as ready for review January 15, 2026 05:53
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

dsarno added a commit to dsarno/unity-mcp that referenced this pull request Jan 15, 2026
The allowNudge parameter was attempting to fix Unity 6 compilation loops
by skipping QueuePlayerLoopUpdate, but this was not the root cause.

The actual issue (fixed by CoplayDev#559's Unity 6+ preprocessor guard) is that
EditorApplication.update callbacks don't survive domain reloads properly
in Unity 6+.

Since CoplayDev#559 skips WaitForUnityReadyAsync entirely on Unity 6+ when compile
is requested, the allowNudge guard is redundant and can be removed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@dsarno dsarno merged commit 87d0f1d into CoplayDev:main Jan 15, 2026
2 checks passed
dsarno added a commit that referenced this pull request Jan 15, 2026
The allowNudge parameter was attempting to fix Unity 6 compilation loops
by skipping QueuePlayerLoopUpdate, but this was not the root cause.

The actual issue (fixed by #559's Unity 6+ preprocessor guard) is that
EditorApplication.update callbacks don't survive domain reloads properly
in Unity 6+.

Since #559 skips WaitForUnityReadyAsync entirely on Unity 6+ when compile
is requested, the allowNudge guard is redundant and can be removed.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@dsarno
Copy link
Collaborator

dsarno commented Jan 15, 2026

@BlindsidedGames Thanks for the contribution and digging into the issue. I tested it and works fine for me. @Scriptwonder let us know if this fixes it for you too. (I reverted the earlier PR changes that weren't actually doing much).

vbucc added a commit to Studio-Pronto/unity-mcp that referenced this pull request Jan 19, 2026
Upstream changes (v9.0.7 → v9.0.8):
- fix: UIDocument serialization to prevent infinite loops (CoplayDev#586)
- fix: Filter isCompiling false positives in Play mode (CoplayDev#582)
- fix: search inactive objects when setActive=true (CoplayDev#581)
- fix: Add Prefab Stage support for GameObject lookup (CoplayDev#573)
- fix: Prevent infinite compilation loop in Unity 6 (CoplayDev#559)
- fix: parse and validate read_console types (CoplayDev#565)
- fix: Local HTTP server UI check (CoplayDev#556)
- fix: Claude Code HTTP Remote UV path override detection
- fix: ULF detection in Claude licensing (CoplayDev#569)
- chore: Replace asmdef GUID references (CoplayDev#564)
- docs: Streamline README for faster onboarding (CoplayDev#583)
- Many new client configurators (VSCode, Cursor, Windsurf, etc.)

Fork enhancements preserved:
- "find" instruction handler in UnityTypeConverters.cs
- MarkSceneOrPrefabDirty() helper for proper Prefab Stage support
- IsInPrefabStage() and GetPrefabStageRoot() helpers
- #main URL reference (no version tags)
- TestProjects excluded

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recompile/Refresh occuring multiple times in sequence.

2 participants