CoplayDev/unity-mcp · error · InvalidOperationException

A Unity test run is already in progress.

Error message

A Unity test run is already in progress.

What it means

Thrown by TestJobManager.StartJob under a lock when _currentJobId is already set. The job manager enforces a single concurrent test run (single-slot) with a check-and-set inside one lock scope to avoid a TOCTOU race. This is the outer guard; TestRunnerService has its own inner guard (error 58).

Source

Thrown at MCPForUnity/Editor/Services/TestJobManager.cs:337

                LastUpdateUnixMs = started,
                TotalTests = null,
                CompletedTests = 0,
                CurrentTestFullName = null,
                CurrentTestStartedUnixMs = null,
                LastFinishedTestFullName = null,
                LastFinishedUnixMs = null,
                FailuresSoFar = new List<TestJobFailure>(),
                Error = null,
                Result = null,
                InitTimeoutMs = initTimeoutMs
            };

            // Single lock scope for check-and-set to avoid TOCTOU race
            lock (LockObj)
            {
                if (!string.IsNullOrEmpty(_currentJobId))
                {
                    throw new InvalidOperationException("A Unity test run is already in progress.");
                }
                Jobs[jobId] = job;
                _currentJobId = jobId;
            }
            PersistToSessionState(force: true);

            // Kick the run (must be called on main thread; our command handlers already run there).
            Task<TestRunResult> task = MCPServiceLocator.Tests.RunTestsAsync(mode, filterOptions);

            void FinalizeJob(Action finalize)
            {
                // Ensure state mutation happens on main thread to avoid Unity API surprises.
                EditorApplication.delayCall += () =>
                {
                    try { finalize(); }
                    catch (Exception ex) { McpLog.Error($"[TestJobManager] Finalize failed: {ex.Message}\n{ex.StackTrace}"); }
                };
            }

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Poll the existing job with get_test_status until it finishes before starting another run.
  2. If the job is a zombie (status stuck, no real run), the finalization safety net should clear it; if not, restart the Unity Editor to reset session state.
  3. Serialize run_tests calls from multiple agents with a queue/lock on the caller side.
Defensive patterns

Strategy: validation

Validate before calling

// Check for an active job before starting a new one.
if (!string.IsNullOrEmpty(TestJobManager.CurrentJobId)) // or expose an IsBusy flag
    return ErrorResponse("A test run is in progress; poll get_test_status before starting another.");

Try / catch

try { return TestJobManager.StartJob(mode, opts); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already in progress"))
{ /* poll existing job; queue or reject the new request */ }

Prevention

When it happens

Trigger: Two run_tests invocations issued before the first finalized (multi-agent concurrency); a previous run whose FinalizeCurrentJobFromRunFinished/continuation never cleared _currentJobId; a PlayMode run still in progress.

Common situations: Two AI agents calling run_tests near-simultaneously; a previous job left as a zombie after a crash; a long PlayMode run still active when a second request arrives.

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/2a816c83c6e5ae57. Report an issue: GitHub.