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
- Flip the channel to private before the move, or pick a public new parent
- Gate drop targets in the UI on visibility: only offer parents whose visibility is Public when the dragged channel is Public
- 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
- Badge channel and drop-target visibility in the UI so mismatches are visible
- Filter drop targets by visibility whenever the dragged channel is public
- Change visibility explicitly rather than via nesting tricks
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
- WrongMoveTarget
- CircularNesting
- can't delete channel while a call is in progress
- channel name can't be blank
- no such invitation
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/debe0eaa114451f9.
Report an issue: GitHub.