{"record":{"id":"3cb523d56a1a269a","repo":"zed-industries/zed","slug":"not-a-room-participant","errorCode":null,"errorMessage":"not a room participant","messagePattern":"not a room participant","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/rooms.rs","lineNumber":1304,"sourceCode":"                .filter(room_participant::Column::RoomId.eq(room_id))\n                .stream(&*tx)\n                .await?;\n\n            let mut is_participant = false;\n            let mut connection_ids = HashSet::default();\n            while let Some(participant) = participants.next().await {\n                let participant = participant?;\n                if let Some(answering_connection) = participant.answering_connection() {\n                    if answering_connection == connection_id {\n                        is_participant = true;\n                    } else {\n                        connection_ids.insert(answering_connection);\n                    }\n                }\n            }\n\n            if !is_participant {\n                Err(anyhow!(\"not a room participant\"))?;\n            }\n\n            Ok(connection_ids)\n        })\n        .await\n    }\n\n    async fn get_channel_room(\n        &self,\n        room_id: RoomId,\n        tx: &DatabaseTransaction,\n    ) -> Result<(Option<channel::Model>, proto::Room)> {\n        let db_room = room::Entity::find_by_id(room_id)\n            .one(tx)\n            .await?\n            .context(\"could not find room\")?;\n\n        let mut db_participants = db_room","sourceCodeStart":1286,"sourceCodeEnd":1322,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/rooms.rs#L1286-L1322","documentation":"Thrown by room_connection_ids when, after scanning all room_participant rows, the calling connection_id does not match any participant's answering_connection. The function both collects the other participants' connection ids and asserts membership; if this connection has no answering connection registered in the room, the caller is not an active participant.","triggerScenarios":"Issuing a room-scoped RPC that resolves other participants (e.g. room messaging/leaving flows) from a connection that never called join_room or whose answering_connection is still null (pending ring not yet answered); the same user participating from another connection/device; reconnected with a new ConnectionId without rejoining.","commonSituations":"Multi-device usage where one window joined the room but another issues the room operation; reconnect logic that reuses old room state with a fresh connection; acting on a room before the join handshake completes.","solutions":["Ensure the connection issuing room operations is the one that joined (answered) the room; rejoin the room from this connection first","Wait for the join_room/rejoin_room response before sending further room-scoped requests on that connection","On reconnect, invalidate room membership state and re-establish it before any dependent RPC"],"exampleFix":"// before\nlet peers = client.room_connection_ids(room_id, connection_id).await?;\n\n// after (verify membership first)\nif !self.joined_rooms.contains(&room_id) {\n    client.join_room(room_id, connection_id).await?;\n    self.joined_rooms.insert(room_id);\n}\nlet peers = client.room_connection_ids(room_id, connection_id).await?;","handlingStrategy":"validation","validationCode":"// Ensure this connection is an active participant before room-scoped calls.\nif !self.joined_rooms.contains(&room_id) {\n    client.join_room(room_id, my_connection_id).await?;\n    self.joined_rooms.insert(room_id);\n}\nlet peers = client.room_connection_ids(room_id, my_connection_id).await?;","typeGuard":null,"tryCatchPattern":"match client.room_connection_ids(room_id, my_connection_id).await {\n    Ok(ids) => Ok(ids),\n    Err(err) if err.to_string().contains(\"not a room participant\") => {\n        client.join_room(room_id, my_connection_id).await?; // rejoin then retry once\n        client.room_connection_ids(room_id, my_connection_id).await\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Issue room operations from the connection that answered the join","Wait for the join_room response before dependent room RPCs","On reconnect, rebuild room membership state per connection before any room-scoped request"],"tags":["rooms","authorization","multi-device","membership","server"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}