{"record":{"id":"be63a705e54d3c96","repo":"zed-industries/zed","slug":"could-not-update-room-participant-role","errorCode":null,"errorMessage":"could not update room participant role","messagePattern":"could not update room participant role","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/rooms.rs","lineNumber":1164,"sourceCode":"                self.check_user_has_signed_cla(user_id, room_id, &tx)\n                    .await?;\n            }\n\n            let result = room_participant::Entity::update_many()\n                .filter(\n                    Condition::all()\n                        .add(room_participant::Column::RoomId.eq(room_id))\n                        .add(room_participant::Column::UserId.eq(user_id)),\n                )\n                .set(room_participant::ActiveModel {\n                    role: ActiveValue::set(Some(role)),\n                    ..Default::default()\n                })\n                .exec(&*tx)\n                .await?;\n\n            if result.rows_affected != 1 {\n                Err(anyhow!(\"could not update room participant role\"))?;\n            }\n            self.get_room(room_id, &tx).await\n        })\n        .await\n    }\n\n    async fn check_user_has_signed_cla(\n        &self,\n        user_id: UserId,\n        room_id: RoomId,\n        tx: &DatabaseTransaction,\n    ) -> Result<()> {\n        let channel = room::Entity::find_by_id(room_id)\n            .one(tx)\n            .await?\n            .context(\"could not find room\")?\n            .find_related(channel::Entity)\n            .one(tx)","sourceCodeStart":1146,"sourceCodeEnd":1182,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/rooms.rs#L1146-L1182","documentation":"Thrown by set_room_participant_role when the UPDATE of role on the (room_id, user_id) participant row does not affect exactly 1 row. The target user must currently be a participant of the room; zero affected rows means they left, were kicked, or never joined.","triggerScenarios":"Admin calls SetRoomParticipantRole for a user who just left the room or disconnected and was cleaned up; role change targeting a user_id that was never in the room; concurrent kick and role change racing so the row is deleted first.","commonSituations":"Admin UI acting on a stale participant list; moderation actions queued while the target user signs off; race between connection-loss cleanup and an admin's command.","solutions":["Refresh the room's participant list immediately before issuing the role change and target only users still present","Treat this error as 'user no longer in room' and update the admin UI accordingly","If the intent is to pre-assign a role, invite the user (or have them rejoin) and then set the role"],"exampleFix":"// before\nclient.set_room_participant_role(room_id, target_user_id, new_role).await?;\n\n// after (check membership first)\nlet room = client.get_room(room_id).await?;\nlet is_present = room.participants.iter().any(|p| p.user_id == target_user_id);\nif !is_present {\n    log::warn!(\"user {target_user_id} is no longer in room {room_id}; skipping role change\");\n    return Ok(());\n}\nclient.set_room_participant_role(room_id, target_user_id, new_role).await?;","handlingStrategy":"validation","validationCode":"// Verify the target is still present before changing their role.\nlet room = client.get_room(room_id).await?;\nif !room.participants.iter().any(|p| p.user_id == target_user_id) {\n    log::warn!(\"target {target_user_id} not in room {room_id}; skipping role change\");\n    return Ok(());\n}\nclient.set_room_participant_role(room_id, target_user_id, role).await?;","typeGuard":"fn is_room_participant(room: &proto::Room, user_id: u64) -> bool {\n    room.participants.iter().any(|p| p.user_id == user_id)\n}","tryCatchPattern":"match client.set_room_participant_role(room_id, target_user_id, role).await {\n    Ok(room) => Ok(room),\n    Err(err) if err.to_string().contains(\"could not update room participant role\") => {\n        self.refresh_participants(room_id); // target left; resync UI\n        Ok(None)\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Refresh the participant list immediately before admin role actions","Design admin UI so actions on departed users are no-ops, not errors","Handle kick/leave events by removing users from pending moderation queues"],"tags":["rooms","roles","admin","race-condition","server"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}