can1357/oh-my-pi · error
${argv[0]} did not report a tunnel URL within ${READY_TIMEOU
Error message
${argv[0]} did not report a tunnel URL within ${READY_TIMEOUT_MS / 1000}s What it means
spawnUrlTunnel waits up to READY_TIMEOUT_MS (30 seconds) for the tunnel binary to print a line matching the ready pattern (e.g. a trycloudflare.com URL). If the process stays alive the whole time but never emits a recognizable URL, the child is killed and this timeout error is thrown. It means the tunnel did not become ready in time or its output format is not understood by the parser.
Source
Thrown at packages/coding-agent/src/blob-broker/exposure.ts:284
baseUrl = normalizeBaseUrl(url);
break;
}
}
scanned = text.lastIndexOf("\n") + 1;
}
// The URL banner can precede edge registration (cloudflared prints the
// hostname before any connection is live); wait for the ready marker.
if (baseUrl !== undefined && (!readyPattern || readyPattern.test(text))) {
return { proc, baseUrl };
}
}
if (exitCode !== null) {
throw new Error(`${argv[0]} exited with code ${exitCode} before reporting a tunnel URL`);
}
await Bun.sleep(150);
}
killTunnelProcess(proc);
throw new Error(`${argv[0]} did not report a tunnel URL within ${READY_TIMEOUT_MS / 1000}s`);
}
function processExposure(kind: ExposureKind, baseUrl: string, proc: Bun.Subprocess): ActiveExposure {
proc.unref();
return {
kind,
baseUrl,
exited: proc.exited.then(() => undefined),
stop: () => killTunnelProcess(proc),
};
}
/**
* Keep an authenticated Pinggy tunnel behind its configured stable hostname.
* Random-hostname modes deliberately return their child exit to the broker:
* restarting those would silently invalidate every already-published URL.
*/
function restartingPinggyExposure(baseUrl: string, argv: string[], initialProc: Bun.Subprocess): ActiveExposure {View on GitHub (pinned to 9690622007)
Solutions
- Run the tunnel binary manually with the same argv and inspect its output format; update the parser/ready pattern if the format changed.
- Increase READY_TIMEOUT_MS if the environment is simply slow.
- Pre-authenticate the binary so it never blocks on prompts (e.g. `ngrok config add-authtoken`, `cloudflared tunnel login`, `tailscale up`).
- Check connectivity to the tunnel service from this host.
- Choose a different exposure kind (ssh, direct) that does not depend on this service.
Example fix
// before await spawnUrlTunnel([binary, "tunnel"], parseCloudflaredUrl); // hangs on unauthenticated cloudflared // after // pre-authenticate outside the hot path await $`cloudflared tunnel login`; await spawnUrlTunnel([binary, "tunnel"], parseCloudflaredUrl);
Defensive patterns
Strategy: retry
Validate before calling
const probe = Bun.spawnSync([binary, ...argv.slice(1)], { stdout: "pipe", stderr: "pipe", timeout: 10_000 });
// run once manually and confirm output contains an https:// URL the parser would match Try / catch
try {
const exposure = await startExposure(config);
} catch (err) {
if (err instanceof Error && err.message.includes("did not report a tunnel URL")) {
await Bun.sleep(1_000);
return retryStartExposure(config, 2); // bounded retry for slow networks
}
throw err;
} Prevention
- Pre-authenticate so the binary never blocks on interactive prompts
- Check the installed binary's log format against the parser expectations
- Allow >30s for slow networks or raise the timeout where configurable
- Kill stale tunnel processes that may hold sessions
When it happens
Trigger: startExposure with a tunnel kind where the spawned binary runs for 30+ seconds without printing a line the adapter's readyPattern/parser matches — slow tunnel registration, hung network, or output format changed in a newer binary version.
Common situations: Very slow networks or DNS delays exceeding 30s; cloudflared stuck retrying connections; ngrok interactive prompt waiting on stdin; a newer tunnel binary that changed log format so the parser never matches; tailscale funnel requiring login in a browser.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timed out: {command}
- AnthropicConnectionTimeoutError
- xAI device-code request failed: ${error instanceof Error ? e
- xAI device-code token polling failed: ${error instanceof Err
- options.firstItemErrorMessage ?? options.errorMessage
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2a79f37bc8e21b9c.
Report an issue: GitHub.