{"record":{"id":"d672ce00e4569062","repo":"can1357/oh-my-pi","slug":"argv-0-exited-with-code-exitcode-before-rep","errorCode":null,"errorMessage":"${argv[0]} exited with code ${exitCode} before reporting a tunnel URL","messagePattern":"(.+?) exited with code (.+?) before reporting a tunnel URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/blob-broker/exposure.ts","lineNumber":279,"sourceCode":"\t\tif (text.length > scanned) {\n\t\t\tif (baseUrl === undefined) {\n\t\t\t\tfor (const line of text.slice(scanned).split(\"\\n\")) {\n\t\t\t\t\tconst url = extract(line);\n\t\t\t\t\tif (url) {\n\t\t\t\t\t\tbaseUrl = normalizeBaseUrl(url);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tscanned = text.lastIndexOf(\"\\n\") + 1;\n\t\t\t}\n\t\t\t// The URL banner can precede edge registration (cloudflared prints the\n\t\t\t// hostname before any connection is live); wait for the ready marker.\n\t\t\tif (baseUrl !== undefined && (!readyPattern || readyPattern.test(text))) {\n\t\t\t\treturn { proc, baseUrl };\n\t\t\t}\n\t\t}\n\t\tif (exitCode !== null) {\n\t\t\tthrow new Error(`${argv[0]} exited with code ${exitCode} before reporting a tunnel URL`);\n\t\t}\n\t\tawait Bun.sleep(150);\n\t}\n\tkillTunnelProcess(proc);\n\tthrow new Error(`${argv[0]} did not report a tunnel URL within ${READY_TIMEOUT_MS / 1000}s`);\n}\n\nfunction processExposure(kind: ExposureKind, baseUrl: string, proc: Bun.Subprocess): ActiveExposure {\n\tproc.unref();\n\treturn {\n\t\tkind,\n\t\tbaseUrl,\n\t\texited: proc.exited.then(() => undefined),\n\t\tstop: () => killTunnelProcess(proc),\n\t};\n}\n\n/**","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/blob-broker/exposure.ts#L261-L297","documentation":"spawnUrlTunnel launches a tunnel binary (cloudflared, ngrok, tailscale, etc.) and scans its stdout for a ready-marker URL. This error is thrown when the child process terminates with a non-zero (or any) exit code before ever printing a tunnel URL, so the broker cannot advertise a public base URL. It is the tunnel-binary crash path of the ready-wait loop.","triggerScenarios":"Calling startExposure with a tunnel kind (cloudflared/ngrok/bore/pinggy/devtunnel/zrok/localhost-run) whose binary exits during the 30s READY_TIMEOUT_MS window before emitting a parseable tunnel URL line — e.g. bad auth token, port conflict, invalid flags, or the binary crashes on startup.","commonSituations":"Expired or wrong ngrok authtoken; cloudflared quick-tunnel blocked by network egress rules; bore/zrok server unreachable so the client exits; binary version changed its CLI flags and dies immediately; sandboxed/container environments that cannot open outbound connections.","solutions":["Read the tunnel binary's stderr output from the spawn (it is captured but not in this error) and run the same argv manually to see the real failure.","Verify credentials: ngrok authtoken, cloudflared tunnel token, bore secret.","Confirm the binary runs: check version (`cloudflared --version`) and that CLI flags match the installed version.","Check network egress/firewall allows the tunnel's outbound connection.","Switch exposure kind (e.g. to `direct` on LAN) as a fallback."],"exampleFix":"// before\noptions: { authtoken: \"<old-expired-token>\" }\n// after\n// refresh the token in ngrok dashboard, then\noptions: { authtoken: process.env.NGROK_AUTHTOKEN }","handlingStrategy":"validation","validationCode":"const probe = Bun.spawnSync([binary, \"--version\"], { stderr: \"pipe\" });\nif (probe.exitCode !== 0) throw new Error(`tunnel binary ${binary} not runnable: ${probe.stderr.toString()}`);","typeGuard":null,"tryCatchPattern":"try {\n  const exposure = await startExposure(config);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"exited with code\")) {\n    logger.warn(\"tunnel exited before ready; falling back\", { kind: config.kind });\n    // inspect tunnel stderr or fall back to another exposure kind\n  } else throw err;\n}","preventionTips":["Verify the tunnel binary exists and runs (--version) before startExposure","Pre-authenticate the binary (ngrok authtoken, cloudflared login, tailscale up)","Confirm outbound network egress from the host","Pin/verify binary version so CLI flags match"],"tags":["tunnel","process-exit","network","configuration"],"backgroundTag":"tunnel-process-exited-before-ready","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}