Skip to content

fix(inspector): support Chrome worker debugging#35629

Merged
nathanwhit merged 6 commits into
denoland:mainfrom
nathanwhit:codex/chrome-worker-debugging
Jul 1, 2026
Merged

fix(inspector): support Chrome worker debugging#35629
nathanwhit merged 6 commits into
denoland:mainfrom
nathanwhit:codex/chrome-worker-debugging

Conversation

@nathanwhit

Copy link
Copy Markdown
Member

Related to #35624.

This updates worker inspector handling so Chrome dedicated Node DevTools can discover and attach to Deno workers, then route flattened worker-session protocol messages correctly.

The main changes are:

  • expose worker targets through Target.getTargets;
  • support explicit Target.attachToTarget / Target.detachFromTarget;
  • preserve worker sessionId routing for Chrome flattened CDP sessions;
  • keep worker wait-on-start behavior so debugger; in a worker can pause before subsequent code runs.

Validation:

  • cargo build -p deno
  • target/debug/deno fmt --check tests/unit/inspector_test.ts
  • target/debug/deno test --config tests/config/deno.json --no-check -A tests/unit/inspector_test.ts --filter inspector_worker_
  • target/debug/deno test --config tests/config/deno.json --no-check -A tests/unit/inspector_test.ts --filter inspector_node_worker
  • Manual Chrome smoke test via chrome://inspect: resumed main script, DevTools showed worker [1] paused in Threads and stopped on the worker debugger; statement before after debugger printed.

@nathanwhit
nathanwhit marked this pull request as ready for review June 30, 2026 01:30
@nathanwhit
nathanwhit force-pushed the codex/chrome-worker-debugging branch from f55fd86 to 50f1dcf Compare June 30, 2026 01:39
@bartlomieju

Copy link
Copy Markdown
Member

Worker debugger; pausing is racy — only works by timing luck

I dug into the worker-pause path and found that the headline behavior (debugger; in a worker pauses) is timing-dependent. It reproduces as a heisenbug: it fails normally, but starts "working" the moment you add any logging to the inspector message pump (the extra I/O perturbs the timing).

Root cause: Chrome's dedicated Node DevTools auto-attaches with waitForDebuggerOnStart: false:

Target.setAutoAttach {autoAttach:true, waitForDebuggerOnStart:false}

should_wait_for_debugger_on_worker_start() only returns true when that flag is true, so the worker boots and runs user code immediately, concurrently with Chrome enabling the debugger on the worker session. The worker can execute debugger; before its V8 debugger agent is armed, in which case V8 treats it as a no-op.

CDP trace (worker -> frontend, in emission order) showing the worker running ahead of Debugger.enable:

[worker->fe] Runtime.consoleAPICalled  "Worker received: start"     // onmessage already running
...flood of Debugger.scriptParsed...
[worker->fe] {"id":48,"result":{"debuggerId":...}}                  // Debugger.enable only completes here
[worker->fe] Debugger.paused (at debugger; line)                    // caught only because tracing slowed user code

Relevant detail: Chrome also sends Page.waitForDebugger to the worker, which we reject as method-not-found:

[fe->worker] {"id":43,"method":"Page.waitForDebugger"}
[worker->fe] {"id":43,"error":{"code":-32601,"message":"'Page.waitForDebugger' wasn't found"}}

That's Chrome's intended "hold the worker until I've configured it" signal, which is presumably why it's comfortable sending waitForDebuggerOnStart: false. We ignore it, so nothing holds the worker.

Why it's not a one-line fix: simply waiting whenever auto-attach is enabled (ignoring the flag) would hang debuggers that attach but never send Runtime.runIfWaitingForDebugger to the worker session. The inspector_worker_debugger_statement_not_blackboxed test added in this PR is exactly that shape, so it would deadlock.

Possible directions (your call):

  1. Implement Page.waitForDebugger on the worker to honor Chrome's explicit hold signal.
  2. Pause worker-on-start whenever a target/auto-attach client is attached, releasing on the runIfWaitingForDebugger Chrome does send to the worker session, with a guard so non-resuming clients don't hang.

Happy to help validate whichever approach you pick.

@nathanwhit
nathanwhit merged commit 1afa1a9 into denoland:main Jul 1, 2026
136 checks passed
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.

2 participants