Skip to content

fix(mcp): timeout aborts Mocha runner so next run_test isn't blocked#5551

Open
DavertMik wants to merge 4 commits into4.xfrom
fix/mcp-timeout-aborts-mocha
Open

fix(mcp): timeout aborts Mocha runner so next run_test isn't blocked#5551
DavertMik wants to merge 4 commits into4.xfrom
fix/mcp-timeout-aborts-mocha

Conversation

@DavertMik
Copy link
Copy Markdown
Contributor

Summary

  • Assign pendingRunPromise immediately after the run IIFE in both run_test and run_step_by_step (was only set on the paused branch, so timeouts left it null and cancel saw nothing to abort).
  • Route timeout rejections through cancelRun() instead of letting them bubble out — wrap Promise.race in try/catch + finally, clear the setTimeout, and return a clean status: "failed" response.
  • Make cancelRun() actually abort Mocha: look up the runner via mocha._runner / mocha._previousRunner / mocha.runner and call runner.abort(); recorder.reset() to drop queued tasks. Existing abortRun flag + pausedController.resolveContinue() stay in place for tests stuck on pause().

Why

Without this, a run_test timeout (or any test that hung in pause()) left Mocha's runningNow state stuck. Every subsequent run_test failed with Mocha instance is currently running, and cancel was a no-op because pendingRunPromise was never assigned. The only recovery was reconnecting the MCP server.

Test plan

  • run_test with timeout: 500 against a real Playwright project hung past 500ms → response is { status: "failed", error: "Timeout after 500ms" } (was: hung response, then permanently broken Mocha).
  • Subsequent run_test after the timeout → completes normally (stats: { tests: 1, passes: 1 }).
  • Existing cancel flow during run_step_by_step still works (verified earlier in this session).
  • Reviewer should confirm against their MCP client (Claude Desktop, Cursor, etc.) — the fix is purely server-side.

🤖 Generated with Claude Code

DavertMik and others added 4 commits May 6, 2026 13:52
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>
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.

1 participant