fix(mcp): timeout aborts Mocha runner so next run_test isn't blocked#5551
Open
fix(mcp): timeout aborts Mocha runner so next run_test isn't blocked#5551
Conversation
Previously the run_test / run_step_by_step timeout was just a setTimeout that rejected the race promise — the Mocha runner kept going, the recorder chain stayed queued, listeners stayed attached, and pause sessions kept trapping. Subsequent run_test calls hit "Mocha instance is currently running". cancel didn't help because pendingRunPromise was only assigned in the paused branch, so it saw nothing to cancel. - Assign pendingRunPromise immediately after runPromise creation in both run_test and run_step_by_step (was set only on pause). - Wrap the Promise.race in try/catch + finally; clear the setTimeout and route timeout rejections through cancelRun(); return a clean status: "failed" payload to the client. - Make cancelRun() actually abort: look up the Mocha runner via mocha._runner / _previousRunner / runner and call runner.abort(); recorder.reset() to drop any queued tasks. Existing abortRun + pause release stay in place for stuck-on-pause cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pause-and-abortRun chain alone is enough: resolveContinue releases the current pauseSession, abortRun causes the next pauseNow-queued pauseSession to reject inside setPauseHandler, the rejection propagates through the recorder to codecept.run, and Mocha's runningNow clears naturally. Reaching into mocha._runner / _previousRunner / .runner and calling recorder.reset() were speculative — the timeout repro still clears Mocha state without them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tep_by_step
Both tools had the same Promise.race + try/catch + finally + cancelRun
+ failure-response block. Extracted into raceRunOutcome(runPromise,
timeout) which returns a tagged outcome ({outcome:'paused'|'completed'}
or {outcome:'aborted', error}) so the caller branches once.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tatus Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pendingRunPromiseimmediately after the run IIFE in bothrun_testandrun_step_by_step(was only set on the paused branch, so timeouts left itnullandcancelsaw nothing to abort).cancelRun()instead of letting them bubble out — wrapPromise.raceintry/catch+finally, clear thesetTimeout, and return a cleanstatus: "failed"response.cancelRun()actually abort Mocha: look up the runner viamocha._runner/mocha._previousRunner/mocha.runnerand callrunner.abort();recorder.reset()to drop queued tasks. ExistingabortRunflag +pausedController.resolveContinue()stay in place for tests stuck onpause().Why
Without this, a
run_testtimeout (or any test that hung inpause()) left Mocha'srunningNowstate stuck. Every subsequentrun_testfailed withMocha instance is currently running, andcancelwas a no-op becausependingRunPromisewas never assigned. The only recovery was reconnecting the MCP server.Test plan
run_testwithtimeout: 500against a real Playwright project hung past 500ms → response is{ status: "failed", error: "Timeout after 500ms" }(was: hung response, then permanently broken Mocha).run_testafter the timeout → completes normally (stats: { tests: 1, passes: 1 }).cancelflow duringrun_step_by_stepstill works (verified earlier in this session).🤖 Generated with Claude Code