{"record":{"id":"8b5cfa42a0f1ef5d","repo":"zed-industries/zed","slug":"cannot-unshare-a-project-hosted-by-another-user","errorCode":null,"errorMessage":"cannot unshare a project hosted by another user","messagePattern":"cannot unshare a project hosted by another user","errorType":"validation","errorClass":"Error::Internal","httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/projects.rs","lineNumber":151,"sourceCode":"        &self,\n        project_id: ProjectId,\n        connection: ConnectionId,\n    ) -> Result<TransactionGuard<(bool, Option<proto::Room>, Vec<ConnectionId>)>> {\n        self.project_transaction(project_id, |tx| async move {\n            let guest_connection_ids = self.project_guest_connection_ids(project_id, &tx).await?;\n            let project = project::Entity::find_by_id(project_id)\n                .one(&*tx)\n                .await?\n                .context(\"project not found\")?;\n            let room = if let Some(room_id) = project.room_id {\n                Some(self.get_room(room_id, &tx).await?)\n            } else {\n                None\n            };\n            if project.host_connection()? == connection {\n                return Ok((true, room, guest_connection_ids));\n            }\n            Err(anyhow!(\"cannot unshare a project hosted by another user\"))?\n        })\n        .await\n    }\n\n    /// Updates the worktrees associated with the given project.\n    pub async fn update_project(\n        &self,\n        project_id: ProjectId,\n        connection: ConnectionId,\n        worktrees: &[proto::WorktreeMetadata],\n    ) -> Result<TransactionGuard<(Option<proto::Room>, Vec<ConnectionId>)>> {\n        self.project_transaction(project_id, |tx| async move {\n            let project = project::Entity::find_by_id(project_id)\n                .filter(\n                    Condition::all()\n                        .add(project::Column::HostConnectionId.eq(connection.id as i32))\n                        .add(\n                            project::Column::HostConnectionServerId.eq(connection.owner_id as i32),","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/projects.rs#L133-L169","documentation":"Thrown inside the unshare/delete-project transaction (crates/collab/src/db/queries/projects.rs:151): the caller's connection is compared against the project's recorded host_connection() — the connection that originally shared the project. Only the host connection (which returns early with Ok at projects.rs:147) or the server-admin path may proceed; any other connection, even the same user on a new connection, gets this error.","triggerScenarios":"Calling unshare_project with a connection that is not the one that created the project — the host reconnected (new ConnectionId) and the old connection tries to clean up, a collaborator tries to unshare someone else's project, or the same user shares from a second device.","commonSituations":"Host client reconnects (connection id rotates) while the stale connection's teardown code runs; admin tooling assuming user-level ownership where the check is connection-level; race between host disconnect cleanup and explicit unshare.","solutions":["Send the unshare RPC from the same live connection that shared the project (the one recorded as host_connection_id/host_connection_server_id)","If the host connection is gone, use the server-admin path or wait for the host-disconnect cleanup to remove the project rather than calling as another connection","Check the returned boolean in the Ok path: host connections get Ok(true) meaning 'deleted now', other valid paths get Ok(false) meaning 'handled otherwise' — design callers around that contract"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only the connection that shared the project may unshare it\nlet is_host = project.host_connection_id == connection.id\n    && project.host_connection_server_id == connection.owner_id;\nif !is_host {\n    return Err(anyhow!(\"only the hosting connection can unshare\"));\n}","typeGuard":null,"tryCatchPattern":"match db.unshare_project(project_id, connection).await {\n    Ok((deleted, room, guests)) => Ok((deleted, room, guests)),\n    Err(err) if err.to_string().contains(\"hosted by another user\") => {\n        Err(anyhow!(\"only the host connection can unshare this project\"))\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Track which of your connections shared each project; unshare from that connection","On reconnect (new ConnectionId), re-share to become host again before managing the share","Respect the Ok(bool) contract: false means the server handled removal another way (e.g. host gone)"],"tags":["collab","projects","authorization","connections"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}