BigPizzaV3/CodexPlusPlus · error · anyhow::Error
room is required
Error message
room is required
What it means
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.
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
Example fix
// before
{ "message_type": "register", "room": "", ... }
// after
{ "message_type": "register", "room": "my-room", ... } Defensive patterns
Strategy: validation
Validate before calling
if (!room || !room.trim()) throw new Error('room is required before connecting'); Type guard
const hasRoom = (r: string | undefined | null): r is string => typeof r === 'string' && r.trim().length > 0;
Try / catch
ws.onclose = (e) => { if (String(e.reason).includes('room is required')) promptForRoom(); }; Prevention
- Require the room field in the connection form
- Trim room input before sending
- Persist a validated default room in config
When it happens
Trigger: First WebSocket registration frame (parse_registration, main.rs ~368) with room absent, empty string "", or whitespace-only like " ".
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- 收到未加密数据包
- first message must be registration json
- registration type must be register
- room token mismatch
- token is required
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/f9748f3a26c30951.
Report an issue: GitHub.
Appendix: source
Thrown at apps/codex-plus-mobile-relay/src/main.rs:368
stream.shutdown().await?;
Ok(())
}
fn parse_registration(message: Message) -> anyhow::Result<Registration> {
let text = match message {
Message::Text(text) => text.to_string(),
Message::Binary(bytes) => {
String::from_utf8(bytes.to_vec()).context("binary registration must be utf-8 json")?
}
_ => bail!("first message must be registration json"),
};
let registration: RegisterMessage =
serde_json::from_str(&text).context("registration is not valid json")?;
if registration.message_type != "register" {
bail!("registration type must be register");
}
if registration.room.trim().is_empty() {
bail!("room is required");
}
if registration.token.trim().is_empty() {
bail!("token is required");
}
let role = Role::from_str(®istration.role).context("role must be host or client")?;
Ok(Registration {
role,
room: registration.room,
token: registration.token,
})
}
fn registration_from_uri(path: &str, query: Option<&str>) -> Option<Registration> {
let query = query?;
let role = match path {
"/host" => Some(Role::Host),
"/client" => Some(Role::Client),
"/ws" => query_value(query, "role").and_then(|role| Role::from_str(&role)),View on GitHub (pinned to b1ed92e5e4)