{"record":{"id":"b8ec598f2e464885","repo":"zed-industries/zed","slug":"user-is-not-a-channel-participant-or-channel-does","errorCode":null,"errorMessage":"user is not a channel participant or channel does not exist","messagePattern":"user is not a channel participant or channel does not exist","errorType":"validation","errorClass":"Error::Internal","httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/channels.rs","lineNumber":784,"sourceCode":"                \"user is not a channel member or channel does not exist\"\n            ))?,\n        }\n    }\n\n    /// Returns whether the given user is a participant in the specified channel.\n    pub async fn check_user_is_channel_participant(\n        &self,\n        channel: &channel::Model,\n        user_id: UserId,\n        tx: &DatabaseTransaction,\n    ) -> Result<ChannelRole> {\n        let role = self.channel_role_for_user(channel, user_id, tx).await?;\n        match role {\n            Some(ChannelRole::Admin)\n            | Some(ChannelRole::Member)\n            | Some(ChannelRole::Guest)\n            | Some(ChannelRole::Talker) => Ok(role.unwrap()),\n            Some(ChannelRole::Banned) | None => Err(anyhow!(\n                \"user is not a channel participant or channel does not exist\"\n            ))?,\n        }\n    }\n\n    /// Returns a user's pending invite for the given channel, if one exists.\n    pub async fn pending_invite_for_channel(\n        &self,\n        channel: &channel::Model,\n        user_id: UserId,\n        tx: &DatabaseTransaction,\n    ) -> Result<Option<channel_member::Model>> {\n        let row = channel_member::Entity::find()\n            .filter(channel_member::Column::ChannelId.eq(channel.root_id()))\n            .filter(channel_member::Column::UserId.eq(user_id))\n            .filter(channel_member::Column::Accepted.eq(false))\n            .one(tx)\n            .await?;","sourceCodeStart":766,"sourceCodeEnd":802,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/channels.rs#L766-L802","documentation":"Thrown by check_user_is_channel_participant (crates/collab/src/db/queries/channels.rs:784): the loosest of the three role checks — Admin, Member, Guest and Talker all pass; only Banned users and users with no membership row (None) are rejected.","triggerScenarios":"A banned user attempting participant-level operations (reading/participating in the channel), or a user with no channel_member row at all (never joined, or membership fully removed) invoking a participant-gated RPC.","commonSituations":"Banned users with still-open clients continuing to send events; operations issued against the wrong channel_id where the user never joined; membership rows removed by an admin purge while sessions stayed alive.","solutions":["Ensure the user has joined the channel (a channel_member row exists) before issuing participant operations","If banned, an admin must lift the ban (set role to Guest or higher) before the user can participate again","Double-check the channel_id in the request — the None arm fires when the user simply isn't in that channel"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn can_participate(role: Option<ChannelRole>) -> bool {\n    !matches!(role, None | Some(ChannelRole::Banned))\n}","typeGuard":"fn is_channel_participant(role: Option<ChannelRole>) -> bool {\n    !matches!(role, None | Some(ChannelRole::Banned))\n}","tryCatchPattern":null,"preventionTips":["Require a channel_member row (any non-banned role) before participant operations","On ban, immediately stop sending participant RPCs from the client","Validate channel_id — a user absent from that channel falls into the None arm"],"tags":["collab","channels","authorization","roles"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}