{"record":{"id":"6343d33d54169cf1","repo":"chenglou/pretext","slug":"failed-to-create-chrome-automation-tab-identifi","errorCode":null,"errorMessage":"Failed to create Chrome automation tab: ${identifiers}","messagePattern":"Failed to create Chrome automation tab: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/browser-automation.ts","lineNumber":500,"sourceCode":"    'set targetWindow to front window',\n    'set targetTab to make new tab at end of tabs of targetWindow with properties {URL:\"about:blank\"}',\n  ]\n\n  if (options.foreground === true) {\n    scriptLines.splice(1, 0, 'activate')\n    scriptLines.push('set active tab index of targetWindow to (count of tabs of targetWindow)')\n  }\n\n  scriptLines.push('return (id of targetWindow as string) & \",\" & (id of targetTab as string)')\n  scriptLines.push('end tell')\n\n  const identifiers = options.foreground === true ? runAppleScript(scriptLines) : runBackgroundAppleScript(scriptLines)\n\n  const [windowIdRaw, tabIdRaw] = identifiers.split(',')\n  const windowId = Number.parseInt(windowIdRaw ?? '', 10)\n  const tabId = Number.parseInt(tabIdRaw ?? '', 10)\n  if (!Number.isFinite(windowId) || !Number.isFinite(tabId)) {\n    throw new Error(`Failed to create Chrome automation tab: ${identifiers}`)\n  }\n\n  return {\n    navigate(url) {\n      const navigateLines = [\n        'tell application \"Google Chrome\"',\n        `set targetWindow to first window whose id is ${windowId}`,\n        `set URL of (first tab of targetWindow whose id is ${tabId}) to ${JSON.stringify(url)}`,\n        'end tell',\n      ]\n      if (options.foreground === true) {\n        runAppleScript(navigateLines)\n      } else {\n        runBackgroundAppleScript(navigateLines)\n      }\n    },\n    readLocationUrl() {\n      try {","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/chenglou/pretext/blob/ac49b09b7d83ede19581fa94a8b892b07d309baf/scripts/browser-automation.ts#L482-L518","documentation":"Thrown by createChromeSession after running an AppleScript that creates a new Google Chrome tab. The script is expected to return a string of the form \"windowId,tabId\"; the host splits on \",\" and Number.parseInts both halves. If either parsed value is not finite (NaN), the whole returned identifier string is surfaced in the error. This indicates the AppleScript contract with Chrome broke — the script ran but its output was not the agreed comma-pair.","triggerScenarios":"options.foreground routes to runAppleScript vs runBackgroundAppleScript; whichever returns, its trimmed stdout is split on \",\". Triggers when Chrome returns an empty string, an error string, a single number, or text without a comma — e.g. Chrome is mid-launch and returned a status line, a permission dialog swallowed the real output, or a Chrome version changed the AppleScript dictionary so the `make new tab` / `id of targetTab` calls return a different shape.","commonSituations":"Chrome not installed or not running and AppleScript falls back to launching it cold (first-run returns unexpected output); macOS Automation permission for osascript/terminal controlling Google Chrome was denied or never granted, so osascript prints a stderr-style string to stdout; a Chrome modal (update prompt, default-browser prompt) is blocking the `make new tab`; a Chrome major-version upgrade renamed the tab-id property; running over SSH/headless where Chrome has no GUI session.","solutions":["Run the AppleScript manually with osascript to see what `identifiers` actually contains (the error message prints it verbatim) — that string is the primary diagnostic.","Confirm Google Chrome is fully launched and frontmost before invoking the session; the script makes a window only if none exist, but a half-launched Chrome returns malformed output.","Open System Settings > Privacy & Security > Automation and ensure the calling terminal/IDE is allowed to control Google Chrome; re-run after toggling.","Dismiss any Chrome modal dialog (update, restore pages, set-as-default) that can intercept the `make new tab` command, then retry.","If the Chrome version changed the dictionary, update the scriptLines in createChromeSession to match the new `id of tab`/`id of window` shape."],"exampleFix":"// before\nconst identifiers = options.foreground === true ? runAppleScript(scriptLines) : runBackgroundAppleScript(scriptLines)\nconst [windowIdRaw, tabIdRaw] = identifiers.split(',')\nconst windowId = Number.parseInt(windowIdRaw ?? '', 10)\nconst tabId = Number.parseInt(tabIdRaw ?? '', 10)\nif (!Number.isFinite(windowId) || !Number.isFinite(tabId)) {\n  throw new Error(`Failed to create Chrome automation tab: ${identifiers}`)\n}\n// after — surface which half failed and require exactly two parts\nconst parts = identifiers.split(',')\nif (parts.length !== 2) {\n  throw new Error(`Chrome AppleScript returned unexpected identifiers (expected \"windowId,tabId\"): ${JSON.stringify(identifiers)}`)\n}\nconst windowId = Number.parseInt(parts[0] ?? '', 10)\nconst tabId = Number.parseInt(parts[1] ?? '', 10)\nif (!Number.isFinite(windowId) || !Number.isFinite(tabId)) {\n  throw new Error(`Failed to parse Chrome tab ids from ${JSON.stringify(identifiers)} (window=${windowId}, tab=${tabId})`)\n}","handlingStrategy":"validation","validationCode":"// Before createBrowserSession('chrome'), sanity-check the environment\nimport { execFileSync } from 'node:child_process'\nfunction preflightChromeAppleScript(): void {\n  // Chrome must be a known target the osascript dictionary recognises\n  const version = execFileSync('osascript', ['-e', 'tell application \"Google Chrome\" to return version'], { encoding: 'utf8' }).trim()\n  if (!/^\\d+\\./.test(version)) {\n    throw new Error(`Chrome not scriptable via osascript (got: ${version})`)\n  }\n}\n// Then: createBrowserSession('chrome', { foreground: true })","typeGuard":"// Narrow the AppleScript identifier contract before parsing\nfunction isWindowTabPair(raw: string): boolean {\n  const parts = raw.split(',')\n  if (parts.length !== 2) return false\n  return parts.every(p => Number.isFinite(Number.parseInt(p, 10)))\n}\nif (!isWindowTabPair(identifiers)) { /* handle gracefully */ }","tryCatchPattern":"try {\n  const session = createBrowserSession('chrome', { foreground: true })\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Failed to create Chrome automation tab:')) {\n    // identifiers are in the message — surface to operator, do NOT silently retry the same script\n    throw new Error(`Chrome automation unavailable. Check osascript permission and that Chrome is running. Detail: ${error.message}`)\n  }\n  throw error\n}","preventionTips":["Pre-flight Chrome with a one-line osascript version check before opening a session so a missing/unscriptable Chrome fails fast with a clear cause.","Grant macOS Automation permission once via System Settings > Privacy & Security > Automation; the first denial persists across runs until toggled.","Avoid running Chrome automation while Chrome shows a modal (update prompt, restore-session) — dismiss those before invoking.","If running over SSH, ensure a GUI Chrome session exists; AppleScript cannot `make new tab` against a headless Chrome."],"tags":["browser-automation","macos","applescript","chrome","session-init"],"backgroundTag":null,"analyzedSha":"ac49b09b7d83ede19581fa94a8b892b07d309baf","analyzedAt":"2026-08-12T17:03:16.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}