can1357/oh-my-pi · error · Error
TCP port ${host}:${port} was not ready after ${timeoutMs}ms
Error message
TCP port ${host}:${port} was not ready after ${timeoutMs}ms What it means
The TCP connect loop could not establish a connection to the adapter's listening port within timeoutMs. Unlike the exit-based error, the adapter process is still running — it just never accepted a TCP connection in time. This prevents indefinite hangs when an adapter never starts listening.
Source
Thrown at packages/coding-agent/src/dap/client.ts:860
/** Wait for a TCP DAP server and retain the first successful connection. */
async function waitForTcpTransport(
host: string,
port: number,
timeoutMs: number,
proc: { exitCode: number | null },
): Promise<SocketTransport> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (proc.exitCode !== null) {
throw new Error(`Adapter process exited before TCP port ${host}:${port} was ready`);
}
try {
return await connectTcpSocket(host, port);
} catch {
await Bun.sleep(50);
}
}
throw new Error(`TCP port ${host}:${port} was not ready after ${timeoutMs}ms`);
}
/**
* Give the adapter a chance to announce it is listening on `port` before the
* first connect. vscode-js-debug prints `Debug server listening at HOST:PORT`
* to stdout from inside its `listen()` callback; waiting for the port to appear
* there means we only connect once the child genuinely owns the reserved port,
* which closes the WSL2-mirrored ghost-accept window (issue #6055) at its root.
*
* Best-effort: resolves on the banner, on process exit, or on timeout — the
* subsequent connect loop and `proc.exitCode` checks surface real failures, so
* an adapter that never prints a banner still proceeds (just without the gate).
* Also drains stdout for the wait's duration: in tcp mode the DAP protocol
* flows over the socket, so nothing else consumes the adapter's stdout.
*
* Exported so tests can drive the gate deterministically with a synthetic stdout.
*/
export async function waitForTcpServerListening(View on GitHub (pinned to 9690622007)
Solutions
- Increase timeoutMs for the TCP connect wait in the launch/connect call
- Verify host and port match what the adapter actually announces on stdout (e.g. 'Debug server listening at ...')
- Check the adapter is listening: ss/netstat/lsof on the configured port
- Fix network/firewall configuration if the adapter runs in another namespace/container
Example fix
// before await connectTcpPort(adapter, '127.0.0.1', 9229, 3000, proc); // after await connectTcpPort(adapter, '127.0.0.1', 9229, 30_000, proc); // adapter needs longer to listen
Defensive patterns
Strategy: retry
Validate before calling
const open = await Bun.$`lsof -i :${port}`.quiet().nothrow();
// if adapter announced a port on stdout, use that instead of the configured guess Try / catch
try {
return await connectTcpPort(adapter, host, port, timeoutMs, proc);
} catch (err) {
if (String((err as Error).message).includes('was not ready')) {
// adapter alive but never listened: probe the announced port from stdout
const announced = parseAnnouncedPort(adapterStdout);
if (announced && announced !== port) return await connectTcpPort(adapter, host, announced, timeoutMs, proc);
}
throw err;
} Prevention
- Wait for the adapter's stdout announcement ('Debug server listening at HOST:PORT') before the first connect
- Increase timeoutMs for adapters with slow listen startup
- Confirm host/port match (127.0.0.1 vs container/WSL addresses) in both config and launch
- Check firewalls/network namespaces when adapters run outside the host netns
When it happens
Trigger: Adapter runs but never calls listen() on the configured port within timeoutMs; connecting to the wrong host/port so every connectTcpSocket attempt fails; firewall or network namespace blocking 127.0.0.1:port; adapter listening on a different interface than probed.
Common situations: vscode-js-debug or debugpy slow to print/listen under load exceeding the timeout; host/port mismatch between adapter config and launcher; Docker/WSL setups where the port isn't reachable at 127.0.0.1; stale port from a previous crashed run.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
- 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/e6ba8dc665eeafa8.
Report an issue: GitHub.