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

  1. Provide a non-empty room identifier in the registration message
  2. Trim and check the room value in the client before connecting
  3. 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

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


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(&registration.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)