{"record":{"id":"fd219210ad95a90b","repo":"BigPizzaV3/CodexPlusPlus","slug":"token-is-required","errorCode":null,"errorMessage":"token is required","messagePattern":"token is required","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"apps/codex-plus-mobile-relay/src/main.rs","lineNumber":371,"sourceCode":"\nfn parse_registration(message: Message) -> anyhow::Result<Registration> {\n    let text = match message {\n        Message::Text(text) => text.to_string(),\n        Message::Binary(bytes) => {\n            String::from_utf8(bytes.to_vec()).context(\"binary registration must be utf-8 json\")?\n        }\n        _ => bail!(\"first message must be registration json\"),\n    };\n    let registration: RegisterMessage =\n        serde_json::from_str(&text).context(\"registration is not valid json\")?;\n    if registration.message_type != \"register\" {\n        bail!(\"registration type must be register\");\n    }\n    if registration.room.trim().is_empty() {\n        bail!(\"room is required\");\n    }\n    if registration.token.trim().is_empty() {\n        bail!(\"token is required\");\n    }\n    let role = Role::from_str(&registration.role).context(\"role must be host or client\")?;\n    Ok(Registration {\n        role,\n        room: registration.room,\n        token: registration.token,\n    })\n}\n\nfn registration_from_uri(path: &str, query: Option<&str>) -> Option<Registration> {\n    let query = query?;\n    let role = match path {\n        \"/host\" => Some(Role::Host),\n        \"/client\" => Some(Role::Client),\n        \"/ws\" => query_value(query, \"role\").and_then(|role| Role::from_str(&role)),\n        _ => None,\n    }?;\n    let room = query_value(query, \"room\")?;","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/apps/codex-plus-mobile-relay/src/main.rs#L353-L389","documentation":"parse_registration validates the first WebSocket message from a relay client; after successfully decoding a register envelope, it found registration.token empty or missing. Without a token the relay cannot authenticate or namespace the connection, so the handshake is rejected before any proxying happens.","triggerScenarios":"First WebSocket registration frame (parse_registration, main.rs ~371) with token missing, \"\", or whitespace-only.","commonSituations":"Token not yet generated/copied from the host; env var or config holding the token is unset; token accidentally cleared when editing client settings.","solutions":["Include the valid relay token in the registration message","Regenerate/copy the token from the host and reconfigure the client","Check that the token env var/config key is actually set and non-blank before connecting"],"exampleFix":"// before\n{ \"message_type\": \"register\", \"room\": \"r1\", \"token\": \"\", \"role\": \"client\" }\n// after\n{ \"message_type\": \"register\", \"room\": \"r1\", \"token\": \"<relay-token>\", \"role\": \"client\" }","handlingStrategy":"validation","validationCode":"if (!token || !token.trim()) throw new Error('relay token is required before connecting');","typeGuard":"const hasToken = (t: string | undefined | null): t is string => typeof t === 'string' && t.trim().length > 0;","tryCatchPattern":"ws.onclose = (e) => { if (String(e.reason).includes('token is required')) promptForToken(); };","preventionTips":["Copy the token from the host before client connect","Fail fast client-side when the token env/config is unset","Never send registration with blank token placeholders"],"tags":["websocket","relay","authentication"],"backgroundTag":"missing-credentials","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}