can1357/oh-my-pi · error · ToolError

Invalid cmux relay authentication challenge from ${endpoint.

Error message

Invalid cmux relay authentication challenge from ${endpoint.host}:${endpoint.port}

What it means

After TCP connect, the relay must send a JSON challenge line with protocol='cmux-relay-auth', an integer version, the client's relay_id, and a non-empty nonce string. If the first line is not valid JSON, or any field fails these checks (including a relay_id that does not match your credentials), this ToolError is thrown before the HMAC response is computed.

Source

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

			);
		}
		const relayId = payload && typeof payload === "object" && "relay_id" in payload ? payload.relay_id : undefined;
		const relayToken =
			payload && typeof payload === "object" && "relay_token" in payload ? payload.relay_token : undefined;
		const fileCredentials = parseRelayCredentials(relayId, relayToken);
		if (!fileCredentials) {
			throw new ToolError(`Invalid cmux relay auth metadata in ~/.cmux/relay/${endpoint.port}.auth`);
		}
		return fileCredentials;
	}

	async #authenticateRelay(endpoint: RelayEndpoint, credentials: RelayCredentials): Promise<void> {
		const challengeLine = await this.#nextLine(DEFAULT_CONNECT_TIMEOUT_MS);
		let challenge: unknown;
		try {
			challenge = JSON.parse(challengeLine);
		} catch {
			throw new ToolError(`Invalid cmux relay authentication challenge from ${endpoint.host}:${endpoint.port}`);
		}
		if (
			!challenge ||
			typeof challenge !== "object" ||
			!("protocol" in challenge) ||
			challenge.protocol !== "cmux-relay-auth" ||
			!("version" in challenge) ||
			typeof challenge.version !== "number" ||
			!Number.isInteger(challenge.version) ||
			!("relay_id" in challenge) ||
			challenge.relay_id !== credentials.relayId ||
			!("nonce" in challenge) ||
			typeof challenge.nonce !== "string" ||
			challenge.nonce.length === 0
		) {
			throw new ToolError(`Invalid cmux relay authentication challenge from ${endpoint.host}:${endpoint.port}`);
		}

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the host:port is the cmux relay and the relay_id you configured belongs to that relay
  2. Restart/upgrade the relay so it speaks the expected cmux-relay-auth protocol and re-check the challenge format
  3. Capture the raw first line from the relay (netcat or logs) to see what it actually sends and compare against the expected challenge shape

Example fix

// before — credentials from another relay
export CMUX_RELAY_ID=relay-old-instance
// after
export CMUX_RELAY_ID=$(jq -r .relay_id ~/.cmux/relay/8931.auth)
Defensive patterns

Strategy: validation

Validate before calling

// before connecting, confirm the credentials belong to this relay
const auth = JSON.parse(await Bun.file(`~/.cmux/relay/${port}.auth`).text());
if (auth.relay_id !== process.env.CMUX_RELAY_ID) {
  throw new Error('CMUX_RELAY_ID does not match relay auth file — wrong relay?');
}

Type guard

function isAuthChallenge(v: unknown, relayId: string): v is { protocol: 'cmux-relay-auth'; version: number; relay_id: string; nonce: string } {
  if (typeof v !== 'object' || v === null) return false;
  const o = v as Record<string, unknown>;
  return o.protocol === 'cmux-relay-auth' && typeof o.version === 'number' &&
    Number.isInteger(o.version) && o.relay_id === relayId &&
    typeof o.nonce === 'string' && o.nonce.length > 0;
}

Try / catch

try {
  await client.connect();
} catch (err) {
  if (err instanceof ToolError && err.message.includes('Invalid cmux relay authentication challenge')) {
    // verify port points at the auth relay and relay_id matches this instance
  }
  throw err;
}

Prevention

When it happens

Trigger: Relay sends a plaintext banner/greeting instead of the JSON challenge; challenge JSON lacks protocol/version/nonce fields; relay_version protocol mismatch; relay_id in the challenge differs from the configured CMUX_RELAY_ID (connected to the wrong relay or credentials for another relay).

Common situations: Pointing at a port that is not the auth relay (some other service answered first); relay upgraded and changed the challenge protocol/version; using relay credentials from a different relay instance.

Understand the failure class

Related errors


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