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
- Verify the host:port is the cmux relay and the relay_id you configured belongs to that relay
- Restart/upgrade the relay so it speaks the expected cmux-relay-auth protocol and re-check the challenge format
- 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
- Confirm host:port is the cmux relay, not another local service
- Keep relay_id consistent between env vars and the .auth file for the same instance
- Update client and relay together when the auth protocol version changes
- Log the raw first line from the relay when debugging handshake failures
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Cmux relay authentication failed for ${endpoint.host}:${endp
- RPC chunk received before protocol negotiation
- line
- Missing cmux relay auth metadata for ${endpoint.host}:${endp
- Invalid cmux relay auth metadata in ~/.cmux/relay/${endpoint
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/aece929b9c140b32.
Report an issue: GitHub.