{"record":{"id":"aece929b9c140b32","repo":"can1357/oh-my-pi","slug":"invalid-cmux-relay-authentication-challenge-from","errorCode":null,"errorMessage":"Invalid cmux relay authentication challenge from ${endpoint.host}:${endpoint.port}","messagePattern":"Invalid cmux relay authentication challenge from (.+?):(.+?)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/cmux/socket-client.ts","lineNumber":223,"sourceCode":"\t\t\t);\n\t\t}\n\t\tconst relayId = payload && typeof payload === \"object\" && \"relay_id\" in payload ? payload.relay_id : undefined;\n\t\tconst relayToken =\n\t\t\tpayload && typeof payload === \"object\" && \"relay_token\" in payload ? payload.relay_token : undefined;\n\t\tconst fileCredentials = parseRelayCredentials(relayId, relayToken);\n\t\tif (!fileCredentials) {\n\t\t\tthrow new ToolError(`Invalid cmux relay auth metadata in ~/.cmux/relay/${endpoint.port}.auth`);\n\t\t}\n\t\treturn fileCredentials;\n\t}\n\n\tasync #authenticateRelay(endpoint: RelayEndpoint, credentials: RelayCredentials): Promise<void> {\n\t\tconst challengeLine = await this.#nextLine(DEFAULT_CONNECT_TIMEOUT_MS);\n\t\tlet challenge: unknown;\n\t\ttry {\n\t\t\tchallenge = JSON.parse(challengeLine);\n\t\t} catch {\n\t\t\tthrow new ToolError(`Invalid cmux relay authentication challenge from ${endpoint.host}:${endpoint.port}`);\n\t\t}\n\t\tif (\n\t\t\t!challenge ||\n\t\t\ttypeof challenge !== \"object\" ||\n\t\t\t!(\"protocol\" in challenge) ||\n\t\t\tchallenge.protocol !== \"cmux-relay-auth\" ||\n\t\t\t!(\"version\" in challenge) ||\n\t\t\ttypeof challenge.version !== \"number\" ||\n\t\t\t!Number.isInteger(challenge.version) ||\n\t\t\t!(\"relay_id\" in challenge) ||\n\t\t\tchallenge.relay_id !== credentials.relayId ||\n\t\t\t!(\"nonce\" in challenge) ||\n\t\t\ttypeof challenge.nonce !== \"string\" ||\n\t\t\tchallenge.nonce.length === 0\n\t\t) {\n\t\t\tthrow new ToolError(`Invalid cmux relay authentication challenge from ${endpoint.host}:${endpoint.port}`);\n\t\t}\n","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/cmux/socket-client.ts#L205-L241","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before — credentials from another relay\nexport CMUX_RELAY_ID=relay-old-instance\n// after\nexport CMUX_RELAY_ID=$(jq -r .relay_id ~/.cmux/relay/8931.auth)","handlingStrategy":"validation","validationCode":"// before connecting, confirm the credentials belong to this relay\nconst auth = JSON.parse(await Bun.file(`~/.cmux/relay/${port}.auth`).text());\nif (auth.relay_id !== process.env.CMUX_RELAY_ID) {\n  throw new Error('CMUX_RELAY_ID does not match relay auth file — wrong relay?');\n}","typeGuard":"function isAuthChallenge(v: unknown, relayId: string): v is { protocol: 'cmux-relay-auth'; version: number; relay_id: string; nonce: string } {\n  if (typeof v !== 'object' || v === null) return false;\n  const o = v as Record<string, unknown>;\n  return o.protocol === 'cmux-relay-auth' && typeof o.version === 'number' &&\n    Number.isInteger(o.version) && o.relay_id === relayId &&\n    typeof o.nonce === 'string' && o.nonce.length > 0;\n}","tryCatchPattern":"try {\n  await client.connect();\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes('Invalid cmux relay authentication challenge')) {\n    // verify port points at the auth relay and relay_id matches this instance\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["auth","relay","protocol","handshake"],"backgroundTag":"auth-challenge-invalid","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}