{"record":{"id":"9e135fa9aebf589c","repo":"zed-industries/zed","slug":"banned-users-cannot-invite","errorCode":null,"errorMessage":"banned users cannot invite","messagePattern":"banned users cannot invite","errorType":"validation","errorClass":"Error::Internal","httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/rooms.rs","lineNumber":169,"sourceCode":"        calling_connection: ConnectionId,\n        called_user_id: UserId,\n        initial_project_id: Option<ProjectId>,\n    ) -> Result<TransactionGuard<(proto::Room, proto::IncomingCall)>> {\n        self.room_transaction(room_id, |tx| async move {\n            let caller = room_participant::Entity::find()\n                .filter(\n                    room_participant::Column::UserId\n                        .eq(calling_user_id)\n                        .and(room_participant::Column::RoomId.eq(room_id)),\n                )\n                .one(&*tx)\n                .await?\n                .context(\"user is not in the room\")?;\n\n            let called_user_role = match caller.role.unwrap_or(ChannelRole::Member) {\n                ChannelRole::Admin | ChannelRole::Member => ChannelRole::Member,\n                ChannelRole::Guest | ChannelRole::Talker => ChannelRole::Guest,\n                ChannelRole::Banned => return Err(anyhow!(\"banned users cannot invite\").into()),\n            };\n\n            room_participant::ActiveModel {\n                room_id: ActiveValue::set(room_id),\n                user_id: ActiveValue::set(called_user_id),\n                answering_connection_lost: ActiveValue::set(false),\n                participant_index: ActiveValue::NotSet,\n                calling_user_id: ActiveValue::set(calling_user_id),\n                calling_connection_id: ActiveValue::set(calling_connection.id as i32),\n                calling_connection_server_id: ActiveValue::set(Some(ServerId(\n                    calling_connection.owner_id as i32,\n                ))),\n                initial_project_id: ActiveValue::set(initial_project_id),\n                role: ActiveValue::set(Some(called_user_role)),\n\n                id: ActiveValue::NotSet,\n                answering_connection_id: ActiveValue::NotSet,\n                answering_connection_server_id: ActiveValue::NotSet,","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/rooms.rs#L151-L187","documentation":"Thrown by rooms call() when the calling user's room_participant role is ChannelRole::Banned. Before creating a participant row for the called user, the server maps the caller's role to the role the invitee will get (Admin/Member -> Member, Guest/Talker -> Guest); a Banned caller is rejected outright because banned participants may not invite anyone into the room.","triggerScenarios":"CallUser (call RPC) issued with a room_id in which the caller's participant row has role Banned; caller was banned mid-call and then tries to ring another contact into the same room.","commonSituations":"Moderators ban a disruptive user whose client still has the room open; the banned user's UI still shows call/invite buttons for that room; stale room state after being removed from a channel.","solutions":["Have a room Admin unban the caller's user, then retry the call","Create or use a different room the caller legitimately participates in","Refresh room state after moderation events and disable invite/call actions for rooms where the caller is banned"],"exampleFix":"// before\nclient.call_user(room_id, called_user_id, initial_project_id).await?;\n\n// after (guard on own role)\nlet room = client.get_room(room_id).await?;\nlet my_role = room.participants.iter().find(|p| p.user_id == my_user_id).and_then(|p| p.role);\nif my_role == Some(proto::ChannelRole::Banned) {\n    return Err(anyhow!(\"cannot invite into a room you are banned from\"));\n}\nclient.call_user(room_id, called_user_id, initial_project_id).await?;","handlingStrategy":"validation","validationCode":"// Do not attempt to invite/call into rooms where you are banned.\nlet my_role = room.participants.iter()\n    .find(|p| p.user_id == my_user_id)\n    .and_then(|p| p.role);\nif my_role == Some(proto::ChannelRole::Banned) {\n    self.ui.show_toast(\"You are banned from this room\");\n    return Ok(());\n}\nclient.call_user(room_id, called_user_id, initial_project_id).await?;","typeGuard":"fn is_banned(participants: &[proto::RoomParticipant], user_id: u64) -> bool {\n    participants.iter().any(|p| p.user_id == user_id && p.role == Some(proto::ChannelRole::Banned))\n}","tryCatchPattern":"if let Err(err) = client.call_user(room_id, called_user_id, None).await {\n    if err.to_string().contains(\"banned users cannot invite\") {\n        self.disable_call_button(room_id);\n    }\n    return Err(err);\n}","preventionTips":["Disable invite/call actions in rooms where your own role is Banned","Refresh room state after moderation events (kick/ban) and update the UI immediately","Prefer creating a fresh room over reusing one you were removed from"],"tags":["rooms","voice-calls","authorization","banned","server"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}