zed-industries/zed · error · Error::Internal

cannot unshare a project hosted by another user

Error message

cannot unshare a project hosted by another user

What it means

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.

Source

Thrown at crates/collab/src/db/queries/projects.rs:151

        &self,
        project_id: ProjectId,
        connection: ConnectionId,
    ) -> Result<TransactionGuard<(bool, Option<proto::Room>, Vec<ConnectionId>)>> {
        self.project_transaction(project_id, |tx| async move {
            let guest_connection_ids = self.project_guest_connection_ids(project_id, &tx).await?;
            let project = project::Entity::find_by_id(project_id)
                .one(&*tx)
                .await?
                .context("project not found")?;
            let room = if let Some(room_id) = project.room_id {
                Some(self.get_room(room_id, &tx).await?)
            } else {
                None
            };
            if project.host_connection()? == connection {
                return Ok((true, room, guest_connection_ids));
            }
            Err(anyhow!("cannot unshare a project hosted by another user"))?
        })
        .await
    }

    /// Updates the worktrees associated with the given project.
    pub async fn update_project(
        &self,
        project_id: ProjectId,
        connection: ConnectionId,
        worktrees: &[proto::WorktreeMetadata],
    ) -> Result<TransactionGuard<(Option<proto::Room>, Vec<ConnectionId>)>> {
        self.project_transaction(project_id, |tx| async move {
            let project = project::Entity::find_by_id(project_id)
                .filter(
                    Condition::all()
                        .add(project::Column::HostConnectionId.eq(connection.id as i32))
                        .add(
                            project::Column::HostConnectionServerId.eq(connection.owner_id as i32),

View on GitHub (pinned to bc538def45)

Solutions

  1. Send the unshare RPC from the same live connection that shared the project (the one recorded as host_connection_id/host_connection_server_id)
  2. 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
  3. 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
Defensive patterns

Strategy: validation

Validate before calling

// Only the connection that shared the project may unshare it
let is_host = project.host_connection_id == connection.id
    && project.host_connection_server_id == connection.owner_id;
if !is_host {
    return Err(anyhow!("only the hosting connection can unshare"));
}

Try / catch

match db.unshare_project(project_id, connection).await {
    Ok((deleted, room, guests)) => Ok((deleted, room, guests)),
    Err(err) if err.to_string().contains("hosted by another user") => {
        Err(anyhow!("only the host connection can unshare this project"))
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/8b5cfa42a0f1ef5d. Report an issue: GitHub.