{"record":{"id":"0b90c76bd046a86b","repo":"BigPizzaV3/CodexPlusPlus","slug":"registration-type-must-be-register","errorCode":null,"errorMessage":"registration type must be register","messagePattern":"registration type must be register","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"apps/codex-plus-mobile-relay/src/main.rs","lineNumber":365,"sourceCode":"    );\n    stream.write_all(response.as_bytes()).await?;\n    stream.write_all(&body).await?;\n    stream.shutdown().await?;\n    Ok(())\n}\n\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 {","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/apps/codex-plus-mobile-relay/src/main.rs#L347-L383","documentation":"After JSON-parsing the first WebSocket message, the relay validates that RegisterMessage.message_type equals the literal \"register\". Any other message_type value bails, because the relay only accepts registration frames on a fresh connection.","triggerScenarios":"Sending a first frame that is valid JSON but whose message_type field is not \"register\" (e.g. \"Register\", \"reg\", or a data/chat message) during handle_websocket_connection -> parse_registration (main.rs ~365).","commonSituations":"Client sends a chat/data message before registering; case mismatch \"Register\" vs \"register\"; outdated client using an older protocol message_type value.","solutions":["Set message_type to exactly \"register\" in the first message","Update the client library to the current relay protocol version","Send only the registration frame first; send other messages after registration succeeds"],"exampleFix":"// before\n{ \"message_type\": \"Register\", \"room\": \"r1\", \"token\": \"t\", \"role\": \"client\" }\n// after\n{ \"message_type\": \"register\", \"room\": \"r1\", \"token\": \"t\", \"role\": \"client\" }","handlingStrategy":"validation","validationCode":"if (reg.message_type !== 'register') throw new Error('message_type must be \"register\"');","typeGuard":"const isRegisterMessage = (m: unknown): m is RegisterMessage => typeof m === 'object' && m !== null && (m as any).message_type === 'register';","tryCatchPattern":"ws.onclose = (e) => { if (String(e.reason).includes('registration type')) fixMessageTypeAndReconnect(); };","preventionTips":["Use a shared protocol constant for message_type","Keep client and relay protocol versions in sync","Unit-test registration payload serialization"],"tags":["websocket","relay","protocol"],"backgroundTag":"schema-validation-failed","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"}