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 TestRunnerService.RunTestsAsync (inside the held _operationLock) when _runCompletionSource is non-null and not yet completed. This is the runner-level single-flight guard; it complements the job-manager guard (error 57). An orphaned completion source after a crashed run can also trip it.

Source

Thrown at MCPForUnity/Editor/Services/TestRunnerService.cs:199

            }
            finally
            {
                _operationLock.Release();
            }
        }

        public async Task<TestRunResult> RunTestsAsync(TestMode mode, TestFilterOptions filterOptions = null)
        {
            await _operationLock.WaitAsync().ConfigureAwait(true);
            Task<TestRunResult> runTask;
            bool adjustedPlayModeOptions = false;
            bool originalEnterPlayModeOptionsEnabled = false;
            EnterPlayModeOptions originalEnterPlayModeOptions = EnterPlayModeOptions.None;
            try
            {
                if (_runCompletionSource != null && !_runCompletionSource.Task.IsCompleted)
                {
                    throw new InvalidOperationException("A Unity test run is already in progress.");
                }

                if (EditorApplication.isPlaying || EditorApplication.isPlayingOrWillChangePlaymode)
                {
                    throw new InvalidOperationException("Cannot start a test run while the Editor is in or entering Play Mode. Stop Play Mode and try again.");
                }

                if (mode == TestMode.PlayMode)
                {
                    // PlayMode runs transition the editor into play across multiple update ticks. Unity's
                    // built-in pipeline schedules SaveModifiedSceneTask early, but that task uses
                    // EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo which throws once play mode is
                    // active. To minimize that window we pre-save dirty scenes and disable domain reload (so the
                    // MCP bridge stays alive). We do NOT force runSynchronously here because that can freeze the
                    // editor in some projects. If the TestRunner still hits the save task after entering play, the
                    // run can fail; in that case, rerun from a clean Edit Mode state.
                    adjustedPlayModeOptions = EnsurePlayModeRunsWithoutDomainReload(
                        out originalEnterPlayModeOptionsEnabled,

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Wait for the existing run to finish before issuing another (poll via the job manager).
  2. If the TCS is orphaned (no real run active but the guard still throws), restart the Unity Editor to reset the service.
  3. Ensure any PlayMode run reaches RunFinished; if it deadlocks, stop Play Mode and let finalization clear state.
Defensive patterns

Strategy: validation

Validate before calling

// Gate on the runner's busy state before calling RunTestsAsync.
if (EditorApplication.isPlaying || EditorApplication.isPlayingOrWillChangePlaymode)
    return ErrorResponse("Editor is in Play Mode; stop it before running tests.");

Try / catch

try { return await tests.RunTestsAsync(mode, opts); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already in progress"))
{ /* wait for the existing run / restart editor if orphaned */ }

Prevention

When it happens

Trigger: A second RunTestsAsync while a previous run's TCS is incomplete; a previous run that crashed without completing/ disposing its TaskCompletionSource; the PlayMode path failing mid-run and leaving the TCS open.

Common situations: Concurrent run_tests calls; a PlayMode run that errored before RunFinished; editor crash mid-run leaving stale state.

Related errors


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