{"record":{"id":"9df9530ed6c0e0e5","repo":"zed-industries/zed","slug":"could-not-update-room-participant-location","errorCode":null,"errorMessage":"could not update room participant location","messagePattern":"could not update room participant location","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/rooms.rs","lineNumber":1119,"sourceCode":"                        )\n                        .add(\n                            room_participant::Column::AnsweringConnectionServerId\n                                .eq(connection.owner_id as i32),\n                        ),\n                )\n                .set(room_participant::ActiveModel {\n                    location_kind: ActiveValue::set(Some(location_kind)),\n                    location_project_id: ActiveValue::set(location_project_id),\n                    ..Default::default()\n                })\n                .exec(&*tx)\n                .await?;\n\n            if result.rows_affected == 1 {\n                let room = self.get_room(room_id, &tx).await?;\n                Ok(room)\n            } else {\n                Err(anyhow!(\"could not update room participant location\"))?\n            }\n        })\n        .await\n    }\n\n    /// Sets the role of a participant in the given room.\n    pub async fn set_room_participant_role(\n        &self,\n        admin_id: UserId,\n        room_id: RoomId,\n        user_id: UserId,\n        role: ChannelRole,\n    ) -> Result<TransactionGuard<proto::Room>> {\n        self.room_transaction(room_id, |tx| async move {\n            room_participant::Entity::find()\n                .filter(\n                    Condition::all()\n                        .add(room_participant::Column::RoomId.eq(room_id))","sourceCodeStart":1101,"sourceCodeEnd":1137,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/rooms.rs#L1101-L1137","documentation":"Thrown by set_room_participant_location when the UPDATE of location_kind/location_project_id on room_participant affects a row count other than 1. The UPDATE targets the caller's own participant row in the room, so a miss means the server has no participant row (with answering connection) for this connection: the user is not currently joined to the room.","triggerScenarios":"Sending a location/presence update for a room the user was kicked from or left; sending it before join_room completed; sending after a reconnect where rejoin failed or was skipped; sending for a room that was deleted.","commonSituations":"Presence updates queued and flushed after leaving a room; UI still showing a room as active after a kick event was missed; reconnect logic that restores the UI but forgets to rejoin rooms before emitting presence.","solutions":["Only send location updates for rooms recorded as joined on this connection, and clear that record on leave/kick/room-updated events","On this error, silently drop the update (or attempt a rejoin) rather than surfacing it to the user","Ensure join_room/rejoin_room succeeds before the first presence update for that room"],"exampleFix":"// before\nclient.set_room_participant_location(room_id, location).await?;\n\n// after (track membership, ignore stale updates)\nif self.joined_rooms.contains(&room_id) {\n    if let Err(err) = client.set_room_participant_location(room_id, location).await {\n        if err.to_string().contains(\"could not update room participant location\") {\n            self.joined_rooms.remove(&room_id); // membership was lost\n        } else {\n            return Err(err);\n        }\n    }\n}","handlingStrategy":"try-catch","validationCode":"// Send presence only for rooms joined on this connection.\nif !self.joined_rooms.contains(&room_id) {\n    return Ok(());\n}\nclient.set_room_participant_location(room_id, location).await?;","typeGuard":null,"tryCatchPattern":"if let Err(err) = client.set_room_participant_location(room_id, location).await {\n    if err.to_string().contains(\"could not update room participant location\") {\n        self.joined_rooms.remove(&room_id); // membership lost; skip future updates\n        return Ok(());\n    }\n    return Err(err);\n}","preventionTips":["Drive presence updates from a per-connection joined-rooms set","Clear membership on leave/kick/room-updated events","Never send location updates during the join handshake before it completes"],"tags":["rooms","presence","state","race-condition","server"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}