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

can't delete channel while a call is in progress

Error message

can't delete channel while a call is in progress

What it means

Thrown by delete_channel when any room under the channel (or any of its descendant channels collected into channels_to_remove) still has rows in room_participant (crates/collab/src/db/queries/channels.rs:263-272). The count query inner-joins room on ChannelId, so a single active participant anywhere in the subtree blocks the whole deletion inside the transaction.

Source

Thrown at crates/collab/src/db/queries/channels.rs:272

                .await?;

            let channels_to_remove = self
                .get_channel_descendants_excluding_self([&channel], &tx)
                .await?
                .into_iter()
                .map(|channel| channel.id)
                .chain(Some(channel_id))
                .collect::<Vec<_>>();

            let channel_has_active_participants = room_participant::Entity::find()
                .inner_join(room::Entity)
                .filter(room::Column::ChannelId.is_in(channels_to_remove.iter().copied()))
                .count(&*tx)
                .await?
                > 0;

            if channel_has_active_participants {
                Err(anyhow!("can't delete channel while a call is in progress"))?;
            }

            channel::Entity::delete_many()
                .filter(channel::Column::Id.is_in(channels_to_remove.iter().copied()))
                .exec(&*tx)
                .await?;

            Ok((channel.root_id(), channels_to_remove))
        })
        .await
    }

    /// Invites a user to a channel as a member.
    pub async fn invite_channel_member(
        &self,
        channel_id: ChannelId,
        invitee_id: UserId,
        inviter_id: UserId,

View on GitHub (pinned to bc538def45)

Solutions

  1. Have all participants leave the call (or kick them) so room_participant rows for those rooms are deleted, then retry the channel deletion
  2. Check for orphaned room_participant rows (participants whose connection is gone) and clean them up before retrying
  3. If deleting a channel tree, ensure no child channel's room has participants either — the check covers the whole channels_to_remove set
Defensive patterns

Strategy: validation

Validate before calling

// Before delete_channel: confirm no room under the subtree has participants
let busy = rooms_under_channel_subtree
    .iter()
    .any(|room| room.active_participants > 0);
if busy {
    return Err(anyhow!("ask participants to leave the call before deleting"));
}

Try / catch

match db.delete_channel(channel_id, admin_id).await {
    Ok(result) => Ok(result),
    Err(err) if err.to_string().contains("call is in progress") => {
        // surface an actionable 'participants still in call' message to the admin UI
        Err(anyhow!("channel busy: a call is still in progress"))
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: Calling delete_channel (admin RPC) while one or more users are in a call room belonging to the channel or any of its child channels; a crashed client whose room_participant row was never cleaned up on disconnect.

Common situations: Admin deletes a busy channel during a scheduled cleanup; leftover room_participant rows from a server that missed a leave event; CI/integration tests deleting fixture channels without first closing the call rooms.

Related errors


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