zed-industries/zed · error · RpcError

BadPublicNesting

BadPublicNesting

Error message

BadPublicNesting

What it means

ErrorCode::BadPublicNesting from move_channel (crates/collab/src/db/queries/channels.rs:936): the moved channel is public but the new parent is not public. A public channel must remain visible under public ancestors; placing it under a non-public parent would either leak it or break visibility rules, so the move is rejected.

Source

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

            self.check_user_is_channel_admin(&channel, admin_id, &tx)
                .await?;
            let new_parent = self.get_channel_internal(new_parent_id, &tx).await?;

            if new_parent.root_id() != channel.root_id() {
                Err(anyhow!(ErrorCode::WrongMoveTarget))?;
            }

            if new_parent
                .ancestors_including_self()
                .any(|id| id == channel.id)
            {
                Err(anyhow!(ErrorCode::CircularNesting))?;
            }

            if channel.visibility == ChannelVisibility::Public
                && new_parent.visibility != ChannelVisibility::Public
            {
                Err(anyhow!(ErrorCode::BadPublicNesting))?;
            }

            let root_id = channel.root_id();
            let new_parent_path = new_parent.path();
            let old_path = format!("{}{}/", channel.parent_path, channel.id);
            let new_path = format!("{}{}/", new_parent_path, channel.id);
            let new_order = max_order(&new_parent_path, &tx).await? + 1;

            let mut model = channel.into_active_model();
            model.parent_path = ActiveValue::Set(new_parent.path());
            model.channel_order = ActiveValue::Set(new_order);
            let channel = model.update(&*tx).await?;

            let descendent_ids =
                ChannelId::find_by_statement::<QueryIds>(Statement::from_sql_and_values(
                    self.pool.get_database_backend(),
                    "
                    UPDATE channels SET parent_path = REPLACE(parent_path, $1, $2)

View on GitHub (pinned to bc538def45)

Solutions

  1. Flip the channel to private before the move, or pick a public new parent
  2. Gate drop targets in the UI on visibility: only offer parents whose visibility is Public when the dragged channel is Public
  3. If the intent is to hide the channel, change its visibility explicitly rather than nesting it under a private parent

Example fix

// before
client.move_channel(public_channel_id, private_parent_id, admin_id).await?;

// after
if channel.visibility == ChannelVisibility::Public {
    assert_eq!(new_parent.visibility, ChannelVisibility::Public);
}
client.move_channel(channel_id, new_parent_id, admin_id).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Public channels need a public parent
if channel.visibility == ChannelVisibility::Public
    && new_parent.visibility != ChannelVisibility::Public
{
    return Err(anyhow!("public channel requires a public parent"));
}

Try / catch

match db.move_channel(channel_id, new_parent_id, admin_id).await {
    Ok(v) => Ok(v),
    Err(err) if err.to_string().contains("BadPublicNesting") => {
        Err(anyhow!("make the channel private or pick a public parent"))
    }
    Err(err) => Err(err),
}

Prevention

When it happens

Trigger: move_channel(channel_id, new_parent_id, admin_id) where channel.visibility == Public and new_parent.visibility != Public (e.g. dragging a public channel under a private parent).

Common situations: Org restructuring that mixes public and private subtrees; UI not displaying/badging visibility on drop targets, so admins unknowingly drop public under private.

Related errors


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