{"record":{"id":"4251be1f6d892242","repo":"can1357/oh-my-pi","slug":"cmux-browser-open-split-did-not-return-a-surface-i","errorCode":null,"errorMessage":"cmux browser.open_split did not return a surface_id","messagePattern":"cmux browser\\.open_split did not return a surface_id","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/tab-supervisor.ts","lineNumber":440,"sourceCode":"): Promise<AcquireTabResult> {\n\tconst attachedSurface = opts.cmuxSurface ?? browser.surface;\n\tif (attachedSurface?.startsWith(\"surface:\")) {\n\t\tthrow new ToolError(\n\t\t\t\"app.surface must be a surface UUID (e.g. CMUX_SURFACE_ID), not a 'surface:N' ref; omit it to open a new split\",\n\t\t);\n\t}\n\n\tlet surfaceId = attachedSurface;\n\tlet initialUrl = opts.url;\n\tlet ownsSurface = false;\n\ttry {\n\t\tif (!surfaceId) {\n\t\t\tconst params: Record<string, unknown> = { url: opts.url ?? \"about:blank\", focus: false };\n\t\t\tif (process.env.CMUX_WORKSPACE_ID) params.workspace_id = process.env.CMUX_WORKSPACE_ID;\n\t\t\tif (process.env.CMUX_SURFACE_ID) params.surface_id = process.env.CMUX_SURFACE_ID;\n\t\t\tconst result = await browser.client.request(\"browser.open_split\", params, { timeoutMs: opts.timeoutMs });\n\t\t\tif (typeof result.surface_id !== \"string\" || result.surface_id.length === 0) {\n\t\t\t\tthrow new ToolError(\"cmux browser.open_split did not return a surface_id\");\n\t\t\t}\n\t\t\tsurfaceId = result.surface_id;\n\t\t\townsSurface = true;\n\t\t\tif (typeof result.url === \"string\" && result.url.length > 0) initialUrl = result.url;\n\t\t\tif (opts.url) {\n\t\t\t\tawait browser.client.request(\n\t\t\t\t\t\"browser.wait\",\n\t\t\t\t\t{\n\t\t\t\t\t\tsurface_id: surfaceId,\n\t\t\t\t\t\tload_state: mapWaitUntil(opts.waitUntil ?? \"load\"),\n\t\t\t\t\t\ttimeout_ms: opts.timeoutMs,\n\t\t\t\t\t},\n\t\t\t\t\t{ timeoutMs: opts.timeoutMs },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst cmuxTab = new CmuxTab({ client: browser.client, surfaceId, url: initialUrl });","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/tab-supervisor.ts#L422-L458","documentation":"For cmux tabs with no pre-attached surface, the library calls the cmux 'browser.open_split' JSON-RPC method and expects the response to carry a non-empty surface_id string. If the response lacks it, the library throws ToolError because it cannot address or track the created split without an identifier.","triggerScenarios":"The cmux host returns a malformed/empty result from browser.open_split — e.g. an outdated cmux server, wrong RPC surface, or the workspace/surface parameters being rejected silently.","commonSituations":"Running against a cmux daemon version that predates surface_id in open_split responses; connecting to a non-cmux endpoint that answers the method with a bare ack; network proxy stripping response fields.","solutions":["Upgrade/verify the cmux daemon supports browser.open_split returning surface_id.","Check CMUX_WORKSPACE_ID / CMUX_SURFACE_ID env vars point at the correct workspace.","Attach to an existing surface by passing the surface UUID explicitly instead of requesting a new split.","Retry the call — a transient daemon error may have produced a truncated response."],"exampleFix":"// before\nconst tab = await browserTool({ action: \"open\" }); // daemon returns {} -> error\n// after\nawait browserTool({ action: \"open\", cmuxSurface: process.env.CMUX_SURFACE_ID }); // attach to known UUID","handlingStrategy":"retry","validationCode":"// verify cmux daemon is reachable and current\nconst pong = await browser.client.request(\"ping\", {}, { timeoutMs: 5000 });","typeGuard":"function hasSurfaceId(r: unknown): r is { surface_id: string } {\n  return typeof (r as any)?.surface_id === \"string\" && (r as any).surface_id.length > 0;\n}","tryCatchPattern":"try {\n  await openCmuxSplit();\n} catch (e) {\n  if (/did not return a surface_id/.test(e.message)) {\n    await restartCmuxDaemon();\n    await openCmuxSplit(); // retry once\n  } else throw e;\n}","preventionTips":["Keep the cmux daemon updated to a version returning surface_id","Attach to an existing CMUX_SURFACE_ID instead of requesting new splits when possible","Set CMUX_WORKSPACE_ID correctly so the split request lands in a valid workspace"],"tags":["cmux","rpc","protocol"],"backgroundTag":"missing-response-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}