{"record":{"id":"f9748f3a26c30951","repo":"BigPizzaV3/CodexPlusPlus","slug":"room-is-required","errorCode":null,"errorMessage":"room is required","messagePattern":"room is required","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"apps/codex-plus-mobile-relay/src/main.rs","lineNumber":368,"sourceCode":"    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 {\n        \"/host\" => Some(Role::Host),\n        \"/client\" => Some(Role::Client),\n        \"/ws\" => query_value(query, \"role\").and_then(|role| Role::from_str(&role)),","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/apps/codex-plus-mobile-relay/src/main.rs#L350-L386","documentation":"The registration's room field is validated with room.trim().is_empty(); if the room identifier is missing or only whitespace, the relay rejects the registration since a room is required to join/relay messages.","triggerScenarios":"First WebSocket registration frame (parse_registration, main.rs ~368) with room absent, empty string \"\", or whitespace-only like \"   \".","commonSituations":"Client UI where the user left the room field blank; room variable undefined/null serialized as omitted or empty; config template with a placeholder room never filled in.","solutions":["Provide a non-empty room identifier in the registration message","Trim and check the room value in the client before connecting","If room comes from config/env, ensure it is set and not a blank placeholder"],"exampleFix":"// before\n{ \"message_type\": \"register\", \"room\": \"\", ... }\n// after\n{ \"message_type\": \"register\", \"room\": \"my-room\", ... }","handlingStrategy":"validation","validationCode":"if (!room || !room.trim()) throw new Error('room is required before connecting');","typeGuard":"const hasRoom = (r: string | undefined | null): r is string => typeof r === 'string' && r.trim().length > 0;","tryCatchPattern":"ws.onclose = (e) => { if (String(e.reason).includes('room is required')) promptForRoom(); };","preventionTips":["Require the room field in the connection form","Trim room input before sending","Persist a validated default room in config"],"tags":["websocket","relay","validation"],"backgroundTag":"empty-required-field","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"}