can1357/oh-my-pi · error · ToolError
Invalid cmux relay auth metadata in ~/.cmux/relay/${endpoint
Error message
Invalid cmux relay auth metadata in ~/.cmux/relay/${endpoint.port}.auth What it means
The relay auth file ~/.cmux/relay/<port>.auth was readable and valid JSON, but its relay_id/relay_token fields failed parseRelayCredentials: both must be non-empty strings and the token must be an even-length hex string. The client refuses to attempt HMAC auth with malformed credentials.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/socket-client.ts:212
if (environmentCredentials) {
return environmentCredentials;
}
const authPath = path.join(os.homedir(), ".cmux", "relay", `${endpoint.port}.auth`);
let payload: unknown;
try {
payload = await Bun.file(authPath).json();
} catch {
throw new ToolError(
`Missing cmux relay auth metadata for ${endpoint.host}:${endpoint.port}; set CMUX_RELAY_ID/CMUX_RELAY_TOKEN or restore ~/.cmux/relay/${endpoint.port}.auth`,
);
}
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) ||View on GitHub (pinned to 9690622007)
Solutions
- Fix the .auth file so relay_id is a non-empty string and relay_token is a non-empty even-length hex string
- Prefer setting CMUX_RELAY_ID/CMUX_RELAY_TOKEN env vars instead — env credentials take precedence over the file
- Regenerate the file from cmux rather than hand-editing it
Example fix
// before (~/.cmux/relay/8931.auth)
{ "relay_id": "relay-abc", "relay_token": "c28tZS10b2tlbg==" }
// after (token as hex)
{ "relay_id": "relay-abc", "relay_token": "736f6d652d746f6b656e" } Defensive patterns
Strategy: validation
Validate before calling
const auth = JSON.parse(await Bun.file(authPath).text());
const ok = typeof auth.relay_id === 'string' && auth.relay_id.length > 0 &&
typeof auth.relay_token === 'string' && /^[0-9a-f]+$/.test(auth.relay_token) &&
auth.relay_token.length % 2 === 0;
if (!ok) throw new Error('malformed relay auth file — regenerate from cmux'); Type guard
function isRelayAuthPayload(v: unknown): v is { relay_id: string; relay_token: string } {
if (typeof v !== 'object' || v === null) return false;
const o = v as Record<string, unknown>;
return typeof o.relay_id === 'string' && o.relay_id.length > 0 &&
typeof o.relay_token === 'string' && o.relay_token.length > 0 &&
o.relay_token.length % 2 === 0 && /^[0-9a-f]+$/i.test(o.relay_token);
} Try / catch
try {
await client.connect();
} catch (err) {
if (err instanceof ToolError && err.message.includes('Invalid cmux relay auth metadata')) {
// regenerate ~/.cmux/relay/<port>.auth via cmux, or switch to env credentials
}
throw err;
} Prevention
- Never hand-edit the .auth file; regenerate it from cmux
- Store the token as hex, not base64 or raw bytes
- Prefer CMUX_RELAY_ID/CMUX_RELAY_TOKEN env vars, which bypass the file entirely
- Validate field names are exactly relay_id and relay_token
When it happens
Trigger: The .auth file exists but contains missing/empty relay_id or relay_token, a non-hex token (e.g. base64 or raw secret), an odd-length hex token, or wrong JSON field names (not relay_id/relay_token).
Common situations: Hand-edited or truncated .auth file; token pasted in base64 rather than hex; cmux version that wrote a different schema than the client expects; copy-paste dropped characters from the token.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Missing cmux relay auth metadata for ${endpoint.host}:${endp
- Failed to load tree-sitter language: {err}
- No credential with id=${id}
- Qwen token/API key is required
- Destination option ${key} must be a string
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/018a3038663a39ca.
Report an issue: GitHub.