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

not authorized to edit projects

Error message

not authorized to edit projects

What it means

Thrown during project join authorization when the requested capability is Capability::ReadWrite but the caller's channel role cannot edit projects (role.can_edit_projects() is false). The role is taken from the caller's room_participant row; a missing participant row defaults to ChannelRole::Banned. Guest, Talker, and Banned roles are read-only in shared projects.

Source

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

            .context("no such project")?;

        let role_from_room = if let Some(room_id) = project.room_id {
            room_participant::Entity::find()
                .filter(room_participant::Column::RoomId.eq(room_id))
                .filter(room_participant::Column::AnsweringConnectionId.eq(connection_id.id))
                .one(tx)
                .await?
                .and_then(|participant| participant.role)
        } else {
            None
        };

        let role = role_from_room.unwrap_or(ChannelRole::Banned);

        match capability {
            Capability::ReadWrite => {
                if !role.can_edit_projects() {
                    return Err(anyhow!("not authorized to edit projects"))?;
                }
            }
            Capability::ReadOnly => {
                if !role.can_read_projects() {
                    return Err(anyhow!("not authorized to read projects"))?;
                }
            }
        }

        Ok((project, role))
    }

    /// Returns the host connection for a read-only request to join a shared project.
    pub async fn host_for_read_only_project_request(
        &self,
        project_id: ProjectId,
        connection_id: ConnectionId,
    ) -> Result<ConnectionId> {

View on GitHub (pinned to bc538def45)

Solutions

  1. Retry the join with Capability::ReadOnly when the caller only needs to view the project
  2. Have a room Admin promote the user to Member/Admin so can_edit_projects() becomes true
  3. Join the room through a normal invitation so the caller gets Member role rather than Guest

Example fix

// before
let (_, replica_id) = client.join_project(project_id, /* read_write */ true).await?;

// after (fall back to read-only join)
let (_, replica_id) = match client.join_project(project_id, true).await {
    Ok(joined) => joined,
    Err(err) if err.to_string().contains("not authorized to edit projects") => {
        client.join_project(project_id, false).await?
    }
    Err(err) => return Err(err),
};
Defensive patterns

Strategy: validation

Validate before calling

// Derive the requested capability from the caller's room role.
let my_role = room.participants.iter()
    .find(|p| p.user_id == my_user_id)
    .and_then(|p| p.role)
    .unwrap_or(proto::ChannelRole::Banned);
let can_edit = matches!(my_role, proto::ChannelRole::Admin | proto::ChannelRole::Member | proto::ChannelRole::Talker);
let capability = if can_edit { Capability::ReadWrite } else { Capability::ReadOnly };
client.join_project(project_id, capability).await?;

Type guard

fn can_edit_projects(role: proto::ChannelRole) -> bool {
    use proto::ChannelRole::*;
    matches!(role, Admin | Member | Talker)
}

Try / catch

match client.join_project(project_id, true).await {
    Ok(joined) => Ok(joined),
    Err(err) if err.to_string().contains("not authorized to edit projects") => {
        client.join_project(project_id, false).await // read-only fallback
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: JoinProject with read_write=true from a user whose room role is Guest or Talker (e.g. invited into a room as a guest); joining a project shared in a room the user was banned from (role defaults to Banned); channel rooms that assign guest roles to non-members.

Common situations: A guest invited to a call tries to edit files in the shared project; permission model changed between client and server versions; admin demoted a user to Talker mid-session and the client reconnects with read-write.

Related errors


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