Repository navigation
run_tests reports false failure or gets stuck when a domain reload lands mid-job (Windows) — related to #1390 #1394
Description
Summary
run_tests (both EditMode and PlayMode, though PlayMode far more often) intermittently reports a false job-level failure — "Test job failed to initialize (tests did not start within timeout)", progress.total: null — while the underlying Unity test run actually completes and passes, confirmed via the NUnit TestResults.xml written to disk. In a smaller number of cases the job instead gets stuck at completed: 0 with total already known, never resolving without run_tests(clear_stuck: true). Once, the bridge itself stopped responding entirely (Unity session not ready ... (ping not answered) on every tool, including execute_code) until the Editor was restarted — the same message reported in #1390.
We ran a structured, 23-call reproduction across four conditions and cross-checked every call's job result against TestResults.xml. Root cause appears to be a domain reload landing during/around a test job, which triggers an MCP↔Unity bridge reconnect; TestJobManager's progress/completion callback seems to stay bound to the pre-reload session and never receives the real completion signal.
Filing this as its own issue (rather than a comment) since the reproduction data is fairly large and platform differs — see "Relation to #1390" below.
Environment
| MCPForUnity | 10.2.0 (com.coplaydev.unity-mcp), revision 30d22075093d1d35dfb0091c1c7550e9ad948577 (confirmed identical to main @ 2026-09-01 for TestJobManager.cs, TestRunnerService.cs, EditorStateCache.cs) |
| Unity | 6000.3.10f1, Windows Editor |
| Transport | default (WebSocket bridge), no --project-scoped-tools |
Symptom A — false failure, real run completes (most common)
run_tests(mode="PlayMode")
-> status: running
-> ... ~120s ...
-> status: failed
error: "Test job failed to initialize (tests did not start within timeout)"
progress.total: null, progress.completed: 0
TestResults.xml on disk for the same time window shows a real, complete, passing run (e.g. testcasecount="80" result="Passed" total="80" passed="80"). Console shows [TestJobManager] Job <id> failed to initialize within 120000ms, auto-failing alongside [TestRunnerNoThrottle] Applied No Throttling → Restored Interaction Mode after test run — i.e. Unity's own test runner lifecycle completed normally.
Reproduced 10 / 23 calls (both EditMode and PlayMode, see table below).
Symptom B — stuck at completed: 0 (less common, more severe)
Same setup, but the job never reaches failed or succeeded — get_test_job returns identical last_update_unix_ms / completed: 0 across 6 consecutive polls (60s wait_timeout each) with total already populated. run_tests(clear_stuck: true) is required to unblock. Reproduced 2 / 23 calls:
EditMode: console showed[TestRunnerNoThrottle] Recovered from domain reload - reapplying No Throttlingtwice during the single stuck job, thenRestored Interaction Mode after test run— the real run did finish (TestResults.xml: 447/447) but the tracker never saw it.PlayMode: same pattern, but this timeTestResults.xmlwas never updated at all andeditor/state.editor.active_scenestayed pinned to the ephemeralInitTestSceneXXXXXXXXscene Unity creates for PlayMode test runs — i.e. that run genuinely never returned control.
Symptom C — full bridge lockup (once, matches #1390's transport symptom)
After several PlayMode retries, run_tests returned "Unity plugin session <id> disconnected while awaiting command_result", then "TimeoutError" on retry. From then on every tool call (run_tests(clear_stuck), execute_code, the editor/state MCP resource) failed with "Unity session not ready for '<op>' (ping not answered); please retry" — repeated 6+ times over several minutes, with each run_tests retry surfacing a different session id. The Editor process itself stayed up and interactive; only restarting it (not killing it) restored the bridge. This is the same (ping not answered) message reported in #1390, though our Editor did not hard-hang (no SIGKILL needed).
Reproduction method
We instrumented every run_tests call with a before/after snapshot of the editor/state MCP resource (compilation.last_domain_reload_after_unix_ms, advice.ready_for_tools, advice.blocking_reasons, staleness.is_stale), the job's get_test_job progress, a console excerpt (TestJobManager, TestRunnerNoThrottle, bridge/reconnect lines), and cross-referenced TestResults.xml's start-time/total/passed for the same window. Four conditions, EditMode + PlayMode:
| Condition | Setup |
|---|---|
| C0 | Idle — no code change since the previous call |
| C1 | .cs file saved immediately before, no explicit refresh |
| C2 | EditorUtility.RequestScriptReload() called and confirmed settled (ready_for_tools == true) before calling run_tests |
| C3 | EditorUtility.RequestScriptReload() called immediately after run_tests returns its job_id, i.e. reload injected while the job is in flight |
| Condition × mode | Calls | False-fail (A) | Stuck (B) | Full lockup (C) |
|---|---|---|---|---|
| C0 EditMode | 2 | 0 | 0 | 0 |
| C0 PlayMode | 2 | 1 | 0 | 0 |
| C1 EditMode | 2 | 0 | 0 | 0 |
| C1 PlayMode | 2 | 2 | 0 | 0 |
| C2 EditMode | 2 | 1 | 0 | 0 |
| C2 PlayMode | 2 | 1 | 0 | 0 |
| C3 EditMode | 5 (2 injected + 3 recovery-check) | 2 | 1 | 0 |
| C3 PlayMode | 6 (2 injected + 4 recovery-check, 1 escalated) | 3 | 1 | 1 |
| Total | 23 | 10 | 2 | 1 |
One of the "no incident" C2 EditMode calls is worth flagging separately: the run_tests call itself returned a transport error ("dropped mid-call", no job_id at all) rather than a job-level failure, while editor/state.tests.last_run and TestResults.xml both confirmed the underlying run had actually completed and passed. An immediate retry succeeded normally. This reads as a milder, self-resolving instance of the same transport hiccup that escalated into Symptom C later on.
In every A/B row, compilation.last_domain_reload_after_unix_ms in the post-call editor/state snapshot fell inside [job.started_unix_ms, job.finished_unix_ms] — including C0/C1 rows, where we did not explicitly request a reload; Unity appears to trigger a reload on its own near test-job start reasonably often (and PlayMode's own Play-mode transition is itself a reload, which is presumably why PlayMode rows dominate).
Root-cause hypothesis (evidence, not verified against source)
- Domain reload lands during/around job start. Supported by the timestamp overlap above; for PlayMode this is close to unavoidable since entering Play Mode triggers one.
- Bridge reconnects as a result. Console shows
[HTTP Reload] Resume attempt 1/6→Resume succeeded→[WebSocket] Preparing to register 35 tool(s) with the bridgeimmediately following an injected reload (sometimes twice for a single job). In the Symptom C lockup,run_testsretries each surfaced a new Unity plugin session id, consistent with repeated reconnect attempts that never fully complete. - The
editor/stateresource cache stalls during the reconnect window —advice.ready_for_tools: false, blocking_reasons: ["stale_status"], withobserved_at_unix_msfrozen (not advancing at all) for anywhere from a few seconds up to ~400s across our samples, before self-healing (except in Symptom C, where it never did). TestJobManager's progress/completion callback appears bound to the pre-reload bridge session and never receives the real completion signal that arrives after reconnect — hence the job either times out (false failure, Symptom A) or is left atcompleted: 0forever (Symptom B).
This reads as the same underlying weakness reported in #1390 (job tracking not surviving a Play-mode-triggered domain reload / bridge disruption), but with different severity: #1390 is a hard, 3/3-reproducible deadlock on Linux/Vulkan requiring SIGKILL, whereas ours is a Windows-side race that mostly self-heals via retry, escalating to a full lockup (Symptom C) only once in 23 calls — resolved by an Editor restart rather than a process kill.
Current workaround (client-side, not a fix)
Before treating a run_tests failure as real: check progress.total == null, check whether editor/state.compilation.last_domain_reload_after_unix_ms falls inside the job's [started_unix_ms, finished_unix_ms] window, and check the console for [TestRunnerNoThrottle] Restored Interaction Mode after test run after the reported failure. If all three hold, confirm advice.ready_for_tools == true and retry once. For the stuck case (Symptom B), run_tests(clear_stuck: true) followed by the same readiness check. For the full-lockup case (Symptom C), an Editor restart was the only thing that worked in our one occurrence.
Trimmed the raw per-call data (JSON snapshots, console excerpts, XML timestamps) down to the summary above; happy to answer follow-up questions about specific data points if it helps triage, but the project this was reproduced in is private so I'd rather not post the full log verbatim.
Activity
Scriptwonder commentedon Oct 4, 2026
This should be fixed in 10.3. Can you double check if the error occurs?