{"record":{"id":"5c7b8035dbb65dfe","repo":"can1357/oh-my-pi","slug":"tab-json-stringify-name-is-bound-to-a-differen","errorCode":null,"errorMessage":"Tab ${JSON.stringify(name)} is bound to a different browser (${describeKind(existing.browser.kind)}). Close it first.","messagePattern":"Tab (.+?) is bound to a different browser \\((.+?)\\)\\. Close it first\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser.ts","lineNumber":270,"sourceCode":"\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync #open(\n\t\tname: string,\n\t\tparams: BrowserParams,\n\t\tdetails: BrowserToolDetails,\n\t\ttimeoutMs: number,\n\t\tsignal?: AbortSignal,\n\t): Promise<AgentToolResult<BrowserToolDetails>> {\n\t\tconst kind = resolveBrowserKind(params, this.session);\n\t\tdetails.browser = kind.kind;\n\n\t\t// If a tab with this name already exists on a different browser kind, fail fast — caller must close first.\n\t\tconst existing = getTab(name);\n\t\tif (existing && !sameBrowserKind(existing.browser.kind, kind)) {\n\t\t\tthrow new ToolError(\n\t\t\t\t`Tab ${JSON.stringify(name)} is bound to a different browser (${describeKind(existing.browser.kind)}). Close it first.`,\n\t\t\t);\n\t\t}\n\n\t\t// The requested timeout must cover the *entire* open — browser\n\t\t// acquisition (CDP discovery/connect), queued tab acquisition, worker\n\t\t// creation, and navigation — not only `acquireTab`. Compose one deadline\n\t\t// from the caller signal and `params.timeout` and thread it through both\n\t\t// stages so a stalled acquisition rejects at the requested boundary.\n\t\t// Capture the deadline start as well: `acquireTab` counts its\n\t\t// worker-init time against this same budget via `deadlineStartMs`\n\t\t// instead of restarting the clock after acquisition.\n\t\tconst deadlineStart = performance.now();\n\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\tconst openSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n\t\ttry {\n\t\t\tconst browser = await untilAborted(openSignal, () =>\n\t\t\t\tacquireBrowser(kind, {","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser.ts#L252-L288","documentation":"Named tabs are bound to a specific browser kind (e.g. managed CDP vs remote). If tab.open is called with a name that already exists on a different browser kind, the tool fails fast with this ToolError rather than silently migrating the tab; the caller must close the existing tab first.","triggerScenarios":"Calling browser open with an existing tab name while switching the browser kind (params or session default changed); reusing a well-known tab name (e.g. 'main') after the environment switched between local and remote browsers; concurrent sessions sharing tab registry names with different kinds.","commonSituations":"Config change mid-session (CDP_ENDPOINT added/removed) altering resolveBrowserKind's result; tests reusing fixed tab names across suites with different browser setups; plugin/agent switching browser preference without closing tabs.","solutions":["Close the existing tab with the same name first, then reopen with the new browser kind","Use a different tab name for the other browser kind","Make the browser kind explicit and consistent (same params/env) for a given tab name","Check resolveBrowserKind inputs (params.browser, session config) to see why the kind differs"],"exampleFix":"// before\nawait browser.run({ action: 'open', name: 'main', url }) // kind switched\n// after\nawait browser.run({ action: 'close', name: 'main' });\nawait browser.run({ action: 'open', name: 'main', url });","handlingStrategy":"validation","validationCode":"const existing = getTab?.(name); // or track locally\nif (existing && existing.browserKind !== requestedKind) {\n\tawait browser.run({ action: 'close', name });\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait browser.run({ action: 'open', name, url });\n} catch (err) {\n\tif (err instanceof ToolError && /bound to a different browser/.test(err.message)) {\n\t\tawait browser.run({ action: 'close', name });\n\t\tawait browser.run({ action: 'open', name, url });\n\t} else throw err;\n}","preventionTips":["Keep a fixed browser kind per tab name","Close old tabs before switching browser configuration","Make the browser kind explicit in params instead of relying on ambient config","Avoid shared fixed tab names across sessions with different browser setups"],"tags":["browser","state-conflict","tab-management"],"backgroundTag":"resource-name-conflict","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}