Skip to content

Conversation

@dsarno
Copy link
Collaborator

@dsarno dsarno commented Jan 19, 2026

Summary

Fixes #549

EditorApplication.isCompiling can return true in Play mode even when Unity is not actually compiling. This causes MCP tools to incorrectly return "error": "busy", "reason": "compiling" errors intermittently.

Changes

  • Added GetActualIsCompiling() helper method in EditorStateCache.cs that:

    • Returns false immediately if EditorApplication.isCompiling is false
    • In Play mode, when EditorApplication.isCompiling is true, double-checks with CompilationPipeline.isCompiling via reflection
    • If CompilationPipeline.isCompiling returns false, treats it as a false positive
    • Falls back to EditorApplication.isCompiling outside Play mode or if reflection fails
  • Updated both usages in EditorStateCache.cs to use the new helper

Test plan

  • Enter Play mode in Unity Editor
  • Make repeated MCP tool calls (find_gameobjects, manage_scene, etc.)
  • Verify no spurious "busy/compiling" errors occur
  • Verify editor_state resource shows is_compiling: false when not actually compiling
  • Verify tools still work correctly in Play mode

Summary by Sourcery

Bug Fixes:

  • Correct is-compiling detection during Play mode by cross-checking EditorApplication.isCompiling with CompilationPipeline.isCompiling to avoid false positives in editor state reporting.

Summary by CodeRabbit

  • Bug Fixes
    • Improved accuracy of compilation state detection in Play mode to eliminate false positives.

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

EditorApplication.isCompiling can return true in Play mode even when Unity
is not actually compiling, causing MCP tools to incorrectly return
"busy/compiling" errors.

This adds a GetActualIsCompiling() helper that double-checks with
CompilationPipeline.isCompiling via reflection when in Play mode to filter
out these false positives.

Fixes CoplayDev#549
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 19, 2026

Reviewer's Guide

Adds a Play-mode-safe helper around Unity’s compilation state and replaces direct EditorApplication.isCompiling usages to avoid false positives that caused MCP tools to report spurious "busy/compiling" errors.

Sequence diagram for MCP tool snapshot using GetActualIsCompiling

sequenceDiagram
    actor Developer
    participant MCPToolClient
    participant MCPToolService
    participant EditorStateCache
    participant EditorApplication
    participant Type
    participant PropertyInfo
    participant CompilationPipeline

    Developer->>MCPToolClient: Invoke MCP tool (find_gameobjects, manage_scene)
    MCPToolClient->>MCPToolService: HTTP request
    MCPToolService->>EditorStateCache: GetSnapshot()
    activate EditorStateCache
    EditorStateCache->>EditorStateCache: GetActualIsCompiling()
    EditorStateCache->>EditorApplication: read isCompiling
    alt isCompiling is false
        EditorStateCache-->>EditorStateCache: return false
    else isCompiling is true and isPlaying is true
        EditorStateCache->>EditorApplication: read isPlaying
        alt isPlaying is true
            EditorStateCache->>Type: GetType(UnityEditor.Compilation.CompilationPipeline, UnityEditor)
            Type-->>EditorStateCache: CompilationPipeline type
            EditorStateCache->>CompilationPipeline: get isCompiling via PropertyInfo
            CompilationPipeline-->>EditorStateCache: actual isCompiling
        else not in Play mode
            EditorStateCache-->>EditorStateCache: trust EditorApplication.isCompiling
        end
    end
    EditorStateCache-->>MCPToolService: snapshot with is_compiling state
    deactivate EditorStateCache
    MCPToolService-->>MCPToolClient: response (no spurious busy/compiling)
    MCPToolClient-->>Developer: Display tool result
Loading

Class diagram for updated EditorStateCache compilation handling

classDiagram
    class EditorStateCache {
        - double _lastUpdateTimeSinceStartup
        - double MinUpdateIntervalSeconds
        - bool _lastIsCompiling
        - long _sequence
        - long _observedUnixMs
        - long _lastCompileStartedUnixMs
        - JObject _cached
        + void OnUpdate()
        + JObject BuildSnapshot(string reason)
        + JObject GetSnapshot()
        - bool GetActualIsCompiling()
    }

    class EditorApplication {
        <<static>>
        + bool isCompiling
        + bool isPlaying
    }

    class CompilationPipeline {
        <<static>>
        + bool isCompiling
    }

    class Type {
        + static Type GetType(string typeName)
    }

    class PropertyInfo {
        + object GetValue(object obj)
    }

    EditorStateCache ..> EditorApplication : uses
    EditorStateCache ..> CompilationPipeline : reflects_to
    EditorStateCache ..> Type : uses_reflection
    EditorStateCache ..> PropertyInfo : uses_reflection
Loading

File-Level Changes

Change Details Files
Introduce GetActualIsCompiling helper that filters out Play-mode false positives from EditorApplication.isCompiling using CompilationPipeline.isCompiling via reflection.
  • Added using System.Reflection to support reflection calls.
  • Implemented GetActualIsCompiling to immediately return false when EditorApplication.isCompiling is false.
  • In Play mode, when EditorApplication.isCompiling is true, reflected UnityEditor.Compilation.CompilationPipeline.isCompiling and used its value as the authoritative compilation state when available.
  • Wrapped reflection in a try/catch and fell back to trusting EditorApplication.isCompiling if reflection fails or outside Play mode.
MCPForUnity/Editor/Services/EditorStateCache.cs
Update editor state cache logic to rely on the new GetActualIsCompiling helper instead of directly querying EditorApplication.isCompiling.
  • Changed the isCompiling calculation in OnUpdate to call GetActualIsCompiling so throttling logic still updates correctly on real compile edges.
  • Changed the isCompiling calculation in BuildSnapshot to call GetActualIsCompiling so snapshots and downstream MCP tools reflect the corrected compile status.
MCPForUnity/Editor/Services/EditorStateCache.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#549 Implement a helper method that correctly determines the actual compilation state by mitigating EditorApplication.isCompiling false positives in Play mode using CompilationPipeline.isCompiling via reflection.
#549 Update EditorStateCache.cs to use the new helper method instead of directly using EditorApplication.isCompiling so that MCP tools no longer report spurious "busy/compiling" errors in Play mode.

Possibly linked issues


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 19, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Fixes a false positive bug where EditorApplication.isCompiling incorrectly reports compilation status during Play mode. Introduces a helper method that validates compilation state via reflection to CompilationPipeline.isCompiling, falling back to the original property if the check fails or not in Play mode.

Changes

Cohort / File(s) Summary
Compilation State Detection
MCPForUnity/Editor/Services/EditorStateCache.cs
Adds GetActualIsCompiling() helper method with reflection-based fallback to detect actual compilation state. Replaces direct EditorApplication.isCompiling calls in OnUpdate() and BuildSnapshot(). Includes System.Reflection using directive and XML documentation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 In Play mode's dance, a quirk appeared,
Compiling false, or so it feared,
Reflection checks what truth will tell,
No more false alarms—all's well!

✨ Finishing touches
  • 📝 Generate docstrings

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.

@dsarno dsarno merged commit f4e441c into CoplayDev:main Jan 19, 2026
1 of 2 checks passed
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 found 2 issues, and left some high level feedback:

  • Consider caching the reflected CompilationPipeline type and isCompiling PropertyInfo in static fields so you avoid paying the reflection cost on every GetActualIsCompiling call during OnUpdate.
  • The catch block in GetActualIsCompiling silently swallows all exceptions; if this can fail in practice, it may be worth at least logging once (or using a guard flag) so unexpected reflection issues are diagnosable while still falling back gracefully.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider caching the reflected `CompilationPipeline` type and `isCompiling` `PropertyInfo` in static fields so you avoid paying the reflection cost on every `GetActualIsCompiling` call during `OnUpdate`.
- The catch block in `GetActualIsCompiling` silently swallows all exceptions; if this can fail in practice, it may be worth at least logging once (or using a guard flag) so unexpected reflection issues are diagnosable while still falling back gracefully.

## Individual Comments

### Comment 1
<location> `MCPForUnity/Editor/Services/EditorStateCache.cs:458-460` </location>
<code_context>
+                        return (bool)prop.GetValue(null);
+                    }
+                }
+                catch
+                {
+                    // If reflection fails, fall back to EditorApplication.isCompiling
+                }
+            }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Catching all exceptions here may hide useful diagnostics when the reflection path breaks.

This bare `catch` will silently hide reflection issues (e.g., type renames, removed APIs), making it hard to detect when this path stops working due to Unity changes. Please at least emit a one-time warning when reflection fails (e.g., guarded by a static flag) so such breakages are visible without flooding the console.

Suggested implementation:

```csharp
                }
                catch (Exception ex)
                {
                    // If reflection fails, fall back to EditorApplication.isCompiling, but emit a one-time warning
                    if (!_compilationReflectionWarningEmitted)
                    {
                        _compilationReflectionWarningEmitted = true;
                        UnityEngine.Debug.LogWarning(
                            "[EditorStateCache] Failed to query UnityEditor.Compilation.CompilationPipeline.isCompiling via reflection. " +
                            "Falling back to EditorApplication.isCompiling. This may indicate a Unity API change.\n" +
                            ex);
                    }
                }
            }

```

1. Add a static flag field to the containing class (likely `EditorStateCache`):

   ```csharp
   private static bool _compilationReflectionWarningEmitted;
   ```

   Place it with the other fields in `EditorStateCache`.

2. Ensure the file has the necessary `using` directives at the top if they are not already present:

   ```csharp
   using System;
   using System.Reflection;
   using UnityEngine;
   ```

3. If your codebase uses a different logging abstraction (e.g., a custom logger instead of `UnityEngine.Debug`), replace `UnityEngine.Debug.LogWarning(...)` with the project-standard logging method.
</issue_to_address>

### Comment 2
<location> `MCPForUnity/Editor/Services/EditorStateCache.cs:464-465` </location>
<code_context>
+                }
+            }
+
+            // Outside Play mode or if reflection failed, trust EditorApplication.isCompiling
+            return true;
+        }
     }
</code_context>

<issue_to_address>
**nitpick:** Final return value relies on earlier guard, making the comment slightly misleading.

The comment says we "trust EditorApplication.isCompiling", but this branch always returns `true`. That only matches the intent because the earlier guard already returned `false` when `EditorApplication.isCompiling` was `false`. Consider either returning `EditorApplication.isCompiling` here, or rephrasing the comment to state that at this point `EditorApplication.isCompiling` is guaranteed to be `true`.
</issue_to_address>

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.

Comment on lines +458 to +460
catch
{
// If reflection fails, fall back to EditorApplication.isCompiling
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Catching all exceptions here may hide useful diagnostics when the reflection path breaks.

This bare catch will silently hide reflection issues (e.g., type renames, removed APIs), making it hard to detect when this path stops working due to Unity changes. Please at least emit a one-time warning when reflection fails (e.g., guarded by a static flag) so such breakages are visible without flooding the console.

Suggested implementation:

                }
                catch (Exception ex)
                {
                    // If reflection fails, fall back to EditorApplication.isCompiling, but emit a one-time warning
                    if (!_compilationReflectionWarningEmitted)
                    {
                        _compilationReflectionWarningEmitted = true;
                        UnityEngine.Debug.LogWarning(
                            "[EditorStateCache] Failed to query UnityEditor.Compilation.CompilationPipeline.isCompiling via reflection. " +
                            "Falling back to EditorApplication.isCompiling. This may indicate a Unity API change.\n" +
                            ex);
                    }
                }
            }
  1. Add a static flag field to the containing class (likely EditorStateCache):

    private static bool _compilationReflectionWarningEmitted;

    Place it with the other fields in EditorStateCache.

  2. Ensure the file has the necessary using directives at the top if they are not already present:

    using System;
    using System.Reflection;
    using UnityEngine;
  3. If your codebase uses a different logging abstraction (e.g., a custom logger instead of UnityEngine.Debug), replace UnityEngine.Debug.LogWarning(...) with the project-standard logging method.

Comment on lines +464 to +465
// Outside Play mode or if reflection failed, trust EditorApplication.isCompiling
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: Final return value relies on earlier guard, making the comment slightly misleading.

The comment says we "trust EditorApplication.isCompiling", but this branch always returns true. That only matches the intent because the earlier guard already returned false when EditorApplication.isCompiling was false. Consider either returning EditorApplication.isCompiling here, or rephrasing the comment to state that at this point EditorApplication.isCompiling is guaranteed to be true.

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>
@dsarno dsarno deleted the fix/iscompiling-false-positive-549 branch January 22, 2026 13:21
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.

EditorApplication.isCompiling False Positive in Play Mode

1 participant