can1357/oh-my-pi · error · ToolError
Cmux relay authentication failed for ${endpoint.host}:${endp
Error message
Cmux relay authentication failed for ${endpoint.host}:${endpoint.port} What it means
After the client signs the challenge nonce with HMAC-SHA256(relayToken) and sends the auth line, the relay must answer with a JSON object { ok: true }. If the response line is not valid JSON, or parses but lacks ok:true, authentication is considered failed and this ToolError is thrown. Note the JSON.parse failure and the ok!==true failure produce the same message.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/socket-client.ts:260
const message = `relay_id=${challenge.relay_id}\nnonce=${challenge.nonce}\nversion=${challenge.version}`;
const key = await globalThis.crypto.subtle.importKey(
"raw",
credentials.relayToken,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
const mac = await globalThis.crypto.subtle.sign("HMAC", key, UTF8.encode(message));
const authLine = JSON.stringify({
relay_id: credentials.relayId,
mac: Buffer.from(mac).toString("hex"),
});
const responseLine = await this.#sendLine(authLine, DEFAULT_CONNECT_TIMEOUT_MS);
let response: unknown;
try {
response = JSON.parse(responseLine);
} catch {
throw new ToolError(`Cmux relay authentication failed for ${endpoint.host}:${endpoint.port}`);
}
if (!response || typeof response !== "object" || !("ok" in response) || response.ok !== true) {
throw new ToolError(`Cmux relay authentication failed for ${endpoint.host}:${endpoint.port}`);
}
}
#waitForConnect(socket: net.Socket): Promise<void> {
const { promise, resolve, reject } = Promise.withResolvers<void>();
const timer = setTimeout(() => {
socket.destroy();
reject(new ToolError(`Failed to connect to cmux socket at ${this.#socketPath}: timed out`));
}, DEFAULT_CONNECT_TIMEOUT_MS);
const cleanup = (): void => {
clearTimeout(timer);
socket.off("connect", onConnect);
socket.off("error", onError);
};
const onConnect = (): void => {View on GitHub (pinned to 9690622007)
Solutions
- Re-sync CMUX_RELAY_TOKEN with the token the relay expects (regenerate from ~/.cmux/relay/<port>.auth or the relay's registration output)
- Confirm relay_id and token come from the same relay instance as the host:port you dial
- Check relay logs for the auth rejection reason and upgrade either side if the HMAC message format (relay_id/nonce/version line) changed
Example fix
// before
class Client {
#token;
async authenticate() { ... }
}
// after — no code change: refresh the token
export CMUX_RELAY_TOKEN=$(jq -r .relay_token ~/.cmux/relay/8931.auth) Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: token must be hex and sourced with the matching relay_id
const token = process.env.CMUX_RELAY_TOKEN;
if (!token || !/^[0-9a-f]+$/i.test(token) || token.length % 2 !== 0) {
throw new Error('CMUX_RELAY_TOKEN missing or not hex');
} Type guard
function isAuthSuccess(v: unknown): v is { ok: true } {
return typeof v === 'object' && v !== null && (v as { ok?: unknown }).ok === true;
} Try / catch
try {
await client.connect();
} catch (err) {
if (err instanceof ToolError && err.message.includes('Cmux relay authentication failed')) {
// token rejected: refresh CMUX_RELAY_TOKEN from the relay, then retry once
}
throw err;
} Prevention
- Re-sync the relay token whenever the relay rotates credentials
- Ensure relay_id and token are a matched pair from one instance
- Keep client and relay versions aligned so the HMAC message format matches
- Check relay-side logs to distinguish wrong-token from wrong-service responses
When it happens
Trigger: Relay rejects the HMAC (wrong CMUX_RELAY_TOKEN or mismatched relay_id/nonce signing), relay answers with an error object {ok:false,...} or plain text, or the connection returned something unexpected (wrong service on the port).
Common situations: Rotated or re-derived relay token that no longer matches what the relay registered; token from a different relay; message-signing format drifted between client and relay versions; a non-relay service listening on that port.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid cmux relay authentication challenge from ${endpoint.
- line
- Missing cmux relay auth metadata for ${endpoint.host}:${endp
- Invalid cmux relay auth metadata in ~/.cmux/relay/${endpoint
- Failed to open auth database at '${dbPath}' after ${maxAttem
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c1b9ce1f46e262e3.
Report an issue: GitHub.