diff --git a/src/browser/errors.test.ts b/src/browser/errors.test.ts index 8f2897a3c..98c977453 100644 --- a/src/browser/errors.test.ts +++ b/src/browser/errors.test.ts @@ -12,6 +12,9 @@ describe('classifyBrowserError', () => { 'CDP connection reset', 'Daemon command failed', 'No window with id: 123', + 'Detached while handling command id=cmd_42', + 'No tab with id: 456', + 'Debugger is not attached to the tab', ]) { const advice = classifyBrowserError(new Error(msg)); expect(advice.kind, `expected "${msg}" → extension-transient`).toBe('extension-transient'); diff --git a/src/browser/errors.ts b/src/browser/errors.ts index 1889b2f2e..e9426763f 100644 --- a/src/browser/errors.ts +++ b/src/browser/errors.ts @@ -45,6 +45,14 @@ const EXTENSION_TRANSIENT_PATTERNS = [ 'CDP connection', 'Daemon command failed', 'No window with id', + // CDP debugger detach race — daemon detaches a command while still serving + // it under concurrent load. Already treated as transient in the e2e + // test harness (tests/e2e/browser-public.test.ts isTransientBrowserDetach), + // but classifyBrowserError did not list it, so CLI runtime would surface + // it as a hard 500 to callers instead of retrying. + 'Detached while handling command', + 'No tab with id', + 'Debugger is not attached to the tab', ] as const; /**