can1357/oh-my-pi · error · ToolError

Failed to connect to cmux socket at ${this.#socketPath}: ${e

Error message

Failed to connect to cmux socket at ${this.#socketPath}: ${err instanceof Error ? err.message : String(err)}

What it means

This is the catch-all wrapper in #openSocket: any failure during socket establishment, relay authentication, or password auth that is not already a ToolError is rethrown as `Failed to connect to cmux socket at <path>: <cause>`. It preserves the original error message (e.g. ECONNREFUSED, ENOENT, EACCES, timeout) while normalizing the type to ToolError.

Source

Thrown at packages/coding-agent/src/tools/browser/cmux/socket-client.ts:170

		socket.on("close", () => this.#handleSocketClose());

		try {
			await this.#waitForConnect(socket);
			if (relayEndpoint && relayCredentials) {
				await this.#authenticateRelay(relayEndpoint, relayCredentials);
			}
			if (this.#password) {
				const line = await this.#sendLine(`auth ${this.#password}`, DEFAULT_CONNECT_TIMEOUT_MS);
				if (line.startsWith("ERROR:") && !line.includes("Unknown command 'auth'")) {
					throw new ToolError(line);
				}
			}
			this.#connected = true;
		} catch (err) {
			this.#connected = false;
			socket.destroy();
			if (err instanceof ToolError) throw err;
			throw new ToolError(
				`Failed to connect to cmux socket at ${this.#socketPath}: ${err instanceof Error ? err.message : String(err)}`,
			);
		}
	}

	#parseRelayEndpoint(): RelayEndpoint | null {
		const value = this.#socketPath.trim();
		if (value.length === 0 || value.startsWith("/")) {
			return null;
		}
		const match = /^(127\.0\.0\.1|localhost):([0-9]+)$/.exec(value);
		if (!match) {
			return null;
		}
		const port = Number.parseInt(match[2] ?? "", 10);
		if (!Number.isInteger(port) || port < 1 || port > 65_535) {
			return null;
		}

View on GitHub (pinned to 9690622007)

Solutions

  1. Confirm the cmux daemon is running and the socket path (CMUX_SOCKET_PATH) is correct and current
  2. Remove a stale socket file and restart the daemon if the path exists but nothing listens
  3. Check permissions on the socket file / relay port, and inspect the appended cause message (ECONNREFUSED/ENOENT/EACCES/timed out) to target the fix

Example fix

// before
CMUX_SOCKET_PATH=/tmp/cmux/old-session.sock
// after (regenerated by the live daemon)
export CMUX_SOCKET_PATH=$(cmux socket-path)
Defensive patterns

Strategy: retry

Validate before calling

import { existsSync } from 'node:fs';
const socketPath = process.env.CMUX_SOCKET_PATH;
if (!socketPath) throw new Error('CMUX_SOCKET_PATH not set');
if (socketPath.startsWith('/') && !existsSync(socketPath)) {
  throw new Error(`socket file missing: ${socketPath} — is the cmux daemon running?`);
}

Try / catch

try {
  await client.connect();
} catch (err) {
  if (err instanceof ToolError && err.message.startsWith('Failed to connect to cmux socket')) {
    if (err.message.includes('timed out')) { /* retry with backoff or check relay reachability */ }
    if (err.message.includes('ENOENT') || err.message.includes('ECONNREFUSED')) { /* start the daemon / fix socket path */ }
  }
  throw err;
}

Prevention

When it happens

Trigger: Unix socket file does not exist (ENOENT), no process listening (ECONNREFUSED), permission denied on the socket (EACCES), TCP connect timeout to a relay host:port, or a non-ToolError thrown inside the connect sequence.

Common situations: cmux daemon not running or crashed; wrong CMUX_SOCKET_PATH; stale socket file after an unclean daemon exit; firewall/host unreachable for 127.0.0.1:<port> relay endpoints; 10-second DEFAULT_CONNECT_TIMEOUT_MS exceeded on a slow relay.

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.

Related errors


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