Skip to content

js / eval can hang the MCP call when the expression returns a non-resolving Promise #38

@apireno

Description

@apireno

Symptom

`js ` and `eval ` (same kernel function under the hood) can hang indefinitely if the user's expression returns a Promise that never resolves — or if the expression triggers a page-level deadlock (broken navigation, beforeunload that waits on something, infinite loop in a handler).

The MCP-side call sees a timeout. The CDP target stays stuck. Worst case the agent gives up and the user sees a stalled tool call.

Surfaced 2026-05-25 by an agent dispatching mouse events on a React-controlled element — the dispatch hung the entire MCP call.

Root cause

`cdp.evaluateJs()` (src/background/cdp_client.ts:509) invokes `Runtime.evaluate` with `awaitPromise: true` and no timeout:

```typescript
await this.send("Runtime.evaluate", {
expression: code,
returnByValue: true,
awaitPromise: true,
});
```

CDP has no built-in cancellation for `Runtime.evaluate`, so a non-resolving Promise parks the whole `await` until the parent client kills the request.

Fix (shipping in 1.3.1)

Add a configurable timeout (default 30s) via `Promise.race`. On timeout, throw a clear error pointing at the cause, and best-effort call `Runtime.terminateExecution` to clean up the stuck evaluator.

Note on naming

`js` and `eval` both route to the same kernel function (src/background/index.ts:1756). The tier difference (`js` is write, `eval` is read) lives at the MCP-server layer (mcp-server/index.ts:58). Both share this hang risk; the fix in `evaluateJs` covers both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions