can1357/oh-my-pi · error

Internal TLS bridge did not bind to a TCP address

Error message

Internal TLS bridge did not bind to a TCP address

What it means

After the internal TLS bridge server listens on an ephemeral port, the code reads tlsServer.address() and requires a TCP AddressInfo object. If Node returns null (server not actually listening) or a string (bound to a Unix socket/pipe), the bridge cannot be connected to via TCP, so it closes the server and throws this error. It is a defensive invariant against unexpected socket binding behavior.

Source

Thrown at packages/coding-agent/src/cli/claude-trace-cli.ts:612

	}

	async #openMitmTunnelAsync(socket: net.Socket, target: ConnectTarget, rest: Buffer): Promise<void> {
		const clientReady = Promise.withResolvers<tls.TLSSocket>();
		const tlsServer = tls.createServer(
			{ cert: CLAUDE_TRACE_DEBUG_CERT, key: CLAUDE_TRACE_DEBUG_KEY, ALPNProtocols: ["http/1.1"] },
			clientTls => {
				this.#track(clientTls);
				clientReady.resolve(clientTls);
			},
		);
		tlsServer.once("error", error => clientReady.reject(error));
		const listening = Promise.withResolvers<void>();
		tlsServer.listen(0, DEFAULT_PROXY_HOST, () => listening.resolve());
		await listening.promise;
		const address = tlsServer.address();
		if (!address || typeof address === "string") {
			tlsServer.close();
			throw new Error("Internal TLS bridge did not bind to a TCP address");
		}
		const bridge = this.#track(net.connect({ host: DEFAULT_PROXY_HOST, port: address.port }));
		const connected = Promise.withResolvers<void>();
		bridge.once("connect", () => connected.resolve());
		bridge.once("error", error => connected.reject(error));
		await connected.promise;
		socket.pipe(bridge);
		bridge.pipe(socket);
		const closeInternalServer = () => tlsServer.close();
		socket.once("close", closeInternalServer);
		bridge.once("close", closeInternalServer);
		if (rest.length > 0) bridge.write(rest);
		const clientTls = await clientReady.promise;
		const upstreamTls = this.#track(
			tls.connect({
				host: target.host,
				port: target.port,
				servername: net.isIP(target.host) ? undefined : target.host,

View on GitHub (pinned to 9690622007)

Solutions

  1. Check that no other code path closes tlsServer before the bridge connect completes (look for close() calls racing startup).
  2. Ensure DEFAULT_PROXY_HOST (127.0.0.1) is bindable — verify no sandbox/firewall blocks loopback binding.
  3. Re-run the trace; if transient, add a small delay or await the actual 'listening' event carrying the address rather than a detached resolver.
  4. If instrumented (e.g. monkey-patched net/tls in tests), restore the unpatched modules before starting the bridge.

Example fix

// before
const address = tlsServer.address();
if (!address || typeof address === "string") { throw new Error("..."); }
// after: wait for the listen callback to supply the port directly
tlsServer.listen(0, DEFAULT_PROXY_HOST, () => listening.resolve());
// ...and in the callback capture: const address = tlsServer.address() as net.AddressInfo; port = address.port;
Defensive patterns

Strategy: type-guard

Type guard

function isAddressInfo(a: ReturnType<typeof tls.Server.prototype.address> | null): a is net.AddressInfo {
  return !!a && typeof a === "object" && typeof (a as net.AddressInfo).port === "number";
}
// usage: if (!isAddressInfo(tlsServer.address())) { tlsServer.close(); throw ... }

Try / catch

try {
  await startTlsBridge();
} catch (err) {
  if (err instanceof Error && err.message === "Internal TLS bridge did not bind to a TCP address") {
    // retry once after cleanup / fall back to external proxy
  } else throw err;
}

Prevention

When it happens

Trigger: tlsServer.address() returns null or a string right after the 'listening' event resolves — e.g. the server closed concurrently, the runtime bound it to a non-TCP handle, or another component closed/replaced the server between listen and address().

Common situations: Running under an environment that intercepts or patches net/tls servers (instrumentation, test harnesses); a race where shutdown logic closes the bridge while startup is still in flight; exotic platforms where localhost binding behaves differently.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/e6e862b5ac89610a. Report an issue: GitHub.