{"record":{"id":"a2db0c4b5caa2a2b","repo":"can1357/oh-my-pi","slug":"app-path-must-be-absolute-got-json-stringify-ex","errorCode":null,"errorMessage":"app.path must be absolute (got ${JSON.stringify(exe)}). Pass the binary inside Foo.app/Contents/MacOS/, not the .app bundle.","messagePattern":"app\\.path must be absolute \\(got (.+?)\\)\\. Pass the binary inside Foo\\.app/Contents/MacOS/, not the \\.app bundle\\.","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/registry.ts","lineNumber":258,"sourceCode":"\t\tconst puppeteer = await loadPuppeteer();\n\t\tconst browser = await puppeteer.connect({\n\t\t\tbrowserURL: cdpUrl,\n\t\t\tdefaultViewport: null,\n\t\t\tprotocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,\n\t\t});\n\t\treturn {\n\t\t\tkey: browserKey(kind),\n\t\t\tkind,\n\t\t\tbrowser,\n\t\t\tcdpUrl,\n\t\t\trefCount: 0,\n\t\t\tstealth: { browserSession: null, override: null },\n\t\t};\n\t}\n\n\tconst exe = kind.path;\n\tif (!path.isAbsolute(exe)) {\n\t\tthrow new ToolError(\n\t\t\t`app.path must be absolute (got ${JSON.stringify(exe)}). Pass the binary inside Foo.app/Contents/MacOS/, not the .app bundle.`,\n\t\t);\n\t}\n\tconst reused = await findReusableCdp(exe, opts.signal);\n\tlet cdpUrl: string;\n\tlet pid: number;\n\tlet subprocess: Subprocess | undefined;\n\tif (reused) {\n\t\tlogger.debug(\"Reusing existing CDP endpoint for attach\", { exe, pid: reused.pid, cdpUrl: reused.cdpUrl });\n\t\tcdpUrl = reused.cdpUrl;\n\t\tpid = reused.pid;\n\t} else {\n\t\tconst killed = await killExistingByPath(exe, opts.signal);\n\t\tif (killed > 0) logger.debug(\"Killed existing instances before attach\", { exe, killed });\n\t\tconst port = await findFreeCdpPort();\n\t\tconst launchArgs = [...(opts.appArgs ?? []), `--remote-debugging-port=${port}`];\n\t\tconst child = Bun.spawn([exe, ...launchArgs], {\n\t\t\tstdout: \"ignore\",","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/registry.ts#L240-L276","documentation":"When opening a local application browser (a real browser binary, e.g. a Chrome.app), openBrowserHandle requires kind.path to be an absolute path to the executable binary inside the bundle. It throws ToolError when a relative path — typically the .app bundle directory itself — is supplied, because reusing/attaching logic (findReusableCdp) and process management need the exact executable.","triggerScenarios":"Configuring a browser app with path like \"/Applications/Google Chrome.app\" (the bundle) or a relative path like \"chrome\" instead of \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\".","commonSituations":"macOS users pointing at the .app bundle; PATH-style executable names relying on shell lookup; hand-edited config files with abbreviated paths.","solutions":["Set app.path to the absolute binary path inside the bundle, e.g. /Applications/Google Chrome.app/Contents/MacOS/Google Chrome (or Chromium/Electron equivalent)","On Linux/Windows use the absolute executable path, e.g. /usr/bin/chromium or C:\\\\Program Files\\\\...\\\\chrome.exe","Resolve the binary programmatically if the path may be relative: path.resolve(exe) before configuring"],"exampleFix":"// before\n{ \"path\": \"/Applications/Chromium.app\" }\n// after\n{ \"path\": \"/Applications/Chromium.app/Contents/MacOS/Chromium\" }","handlingStrategy":"validation","validationCode":"import * as path from \"node:path\";\nfunction assertAbsoluteExe(exe: string): string {\n  if (!path.isAbsolute(exe)) throw new Error(`app.path must be absolute: ${exe}`);\n  if (exe.endsWith(\".app\")) throw new Error(`app.path must be the binary inside ${exe}/Contents/MacOS/, not the .app bundle`);\n  return exe;\n}","typeGuard":"function isAbsoluteBinaryPath(p: string): boolean {\n  return path.isAbsolute(p) && !p.endsWith(\".app\");\n}","tryCatchPattern":null,"preventionTips":["Always configure the executable inside Foo.app/Contents/MacOS/, never the .app bundle itself","Use absolute paths; never rely on PATH lookup for browser binaries","Sanity-check the configured path exists (fs access) before saving the config","On macOS, discover binaries with `mdfind` or the app bundle layout, not `which`"],"tags":["configuration","validation","macos","browser","path"],"backgroundTag":"invalid-config-path","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}