{"record":{"id":"a971f8bd7b2bbf14","repo":"garrytan/gstack","slug":"1","errorCode":"1","errorMessage":"browse newtab exited 1: could not parse tab id from: ${out}","messagePattern":"browse newtab exited 1: could not parse tab id from: (.+?)","errorType":"exception","errorClass":"BrowseClientError","httpStatus":null,"severity":"error","filePath":"make-pdf/src/browseClient.ts","lineNumber":239,"sourceCode":" * Open a new tab. Returns the tabId.\n * Requires `$B newtab --json` to be available (added in the browse flag\n * extension for this feature). If --json isn't supported yet, the fallback\n * parses \"Opened tab N\" from stdout.\n */\nexport function newtab(url?: string): number {\n  const args = [\"newtab\"];\n  if (url) args.push(url);\n  // Try --json first (preferred path for programmatic use)\n  try {\n    const out = runBrowse([...args, \"--json\"]);\n    const parsed = JSON.parse(out);\n    if (typeof parsed.tabId === \"number\") return parsed.tabId;\n  } catch {\n    // Fall back to stdout-string parsing. Brittle, but works on older browse builds.\n  }\n  const out = runBrowse(args);\n  const m = out.match(/tab\\s+(\\d+)/i);\n  if (!m) throw new BrowseClientError(1, \"newtab\", `could not parse tab id from: ${out}`);\n  return parseInt(m[1], 10);\n}\n\n/**\n * Close a tab (by id or the active tab).\n */\nexport function closetab(tabId?: number): void {\n  const args = [\"closetab\"];\n  if (tabId !== undefined) args.push(String(tabId));\n  runBrowse(args);\n}\n\n/**\n * Load raw HTML into a specific tab.\n * Uses --from-file for any payload >4KB (Codex round 2 #3).\n */\nexport function loadHtml(opts: LoadHtmlOptions): void {\n  // Always use --from-file to dodge argv limits. The HTML is almost always >4KB.","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/make-pdf/src/browseClient.ts#L221-L257","documentation":"BrowseClientError(1, 'newtab', ...) thrown when neither the --json path nor the legacy stdout regex `/tab\\s+(\\d+)/i` could extract a tab id from `browse newtab` output. The fallback exists for older browse builds that lack --json; if both fail the client cannot proceed because subsequent calls need a numeric tabId.","triggerScenarios":"Calling newtab(url) where browse is too old to support --json AND its stdout text changed format (e.g. localized output, a new banner line, or a version that prints 'tab id: 7' instead of 'tab 7'). Also when browse exits 0 but prints an error notice to stdout instead of opening a tab.","commonSituations":"A browse build from a different branch that changed the newtab CLI output; non-English locale altering the word 'tab'; a daemon in a bad state that prints a diagnostic to stdout but returns 0; a stdin/stdout capture that strips the relevant line.","solutions":["Upgrade browse to a build that supports `newtab --json` (the preferred path).","Run `browse newtab --json` manually and inspect the output to confirm tabId is present.","If --json works but returns a different key, update the parser in newtab().","Restart the browse daemon to clear the bad-state stdout."],"exampleFix":"// before\nconst out = runBrowse(args);\nconst m = out.match(/tab\\s+(\\d+)/i);\nif (!m) throw new BrowseClientError(1, 'newtab', `could not parse tab id from: ${out}`);\nreturn parseInt(m[1], 10);\n\n// after: prefer --json strictly, broaden fallback regex, log raw output on failure\ntry {\n  const parsed = JSON.parse(runBrowse([...args, '--json']));\n  if (typeof parsed.tabId === 'number') return parsed.tabId;\n  if (typeof parsed.id === 'number') return parsed.id; // alternate key\n} catch { /* legacy */ }\nconst out = runBrowse(args);\nconst m = out.match(/tab\\s*(?:id)?[:\\s]*(\\d+)/i);\nif (!m) throw new BrowseClientError(1, 'newtab', `could not parse tab id from: ${JSON.stringify(out)}`);\nreturn parseInt(m[1], 10);","handlingStrategy":"validation","validationCode":"// Probe browse capabilities once and pick the newtab strategy.\nfunction supportsNewtabJson(): boolean {\n  try {\n    const out = runBrowse(['newtab', '--help']);\n    return /--json/.test(out);\n  } catch {\n    return false;\n  }\n}\n\nconst useJson = supportsNewtabJson();","typeGuard":null,"tryCatchPattern":"try {\n  return newtab(url);\n} catch (e) {\n  if (e instanceof BrowseClientError && e.command === 'newtab' && /could not parse/i.test(e.message)) {\n    // upgrade prompt: this browse build is too old\n    throw new Error('browse build too old for newtab — re-run ./setup');\n  }\n  throw e;\n}","preventionTips":["Use a browse build that supports `newtab --json`.","Restart the daemon if newtab output starts including diagnostic banners.","Pin browse to a known-good version in CI.","Do not localize the browse CLI output (keep C/POSIX locale)."],"tags":["make-pdf","browse","newtab","parsing","version-skew"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}