jamiepine/voicebox · warning · ValueError
Cannot delete the default channel
Error message
Cannot delete the default channel
What it means
Raised as ValueError (HTTP 400) by delete_channel when the channel's is_default=True. The default channel is protected from deletion so an audio output always exists. The guard runs after the lookup but before any DELETE of mappings, so no partial cleanup occurs on rejection. A non-existent channel returns False (surfaced as 404 by the route), not this error.
Source
Thrown at backend/services/channels.py:173
device_ids = [m.device_id for m in device_mappings]
return AudioChannelResponse(
id=channel.id,
name=channel.name,
is_default=channel.is_default,
device_ids=device_ids,
created_at=channel.created_at,
)
async def delete_channel(channel_id: str, db: Session) -> bool:
"""Delete an audio channel."""
channel = db.query(DBAudioChannel).filter_by(id=channel_id).first()
if not channel:
return False
if channel.is_default:
raise ValueError("Cannot delete the default channel")
# Delete device mappings
db.query(DBChannelDeviceMapping).filter_by(channel_id=channel_id).delete()
# Delete profile-channel mappings
db.query(DBProfileChannelMapping).filter_by(channel_id=channel_id).delete()
# Delete channel
db.delete(channel)
db.commit()
return True
async def get_channel_voices(channel_id: str, db: Session) -> List[str]:
"""Get list of profile IDs assigned to a channel."""
mappings = db.query(DBProfileChannelMapping).filter_by(
channel_id=channel_idView on GitHub (pinned to 51f49dea19)
Solutions
- Hide/disable the delete action for the channel with is_default=true in the UI.
- In bulk operations, filter out default channel ids before issuing DELETEs.
- If a different default is needed, seed/migrate a new default rather than deleting this one.
Example fix
// before
for (const id of selectedIds) await api.deleteChannel(id);
// after
for (const id of selectedIds) {
const ch = channels.find(c => c.id === id);
if (ch?.is_default) continue;
await api.deleteChannel(id);
} Defensive patterns
Strategy: validation
Validate before calling
function isDeletableChannel(ch) {
return Boolean(ch) && ch.is_default === false;
}
const targets = selected.filter(isDeletableChannel);
if (!targets.length) { notify('Select a non-default channel to delete'); return; } Type guard
function isRemovableChannel(ch) {
return Boolean(ch) && ch.is_default === false;
} Try / catch
try { await api.deleteChannel(id); }
catch (e) {
if (e.status === 400 && /default channel/.test(e.detail)) notify('Default channel cannot be deleted');
else if (e.status === 404) { dropChannel(id); }
else throw e;
} Prevention
- Hide/disable delete for is_default channels.
- Filter default ids out of bulk delete sets.
- Seed a new default via migration if a different default is required.
When it happens
Trigger: DELETE /channels/{channel_id} where channel_id is the default channel (is_default=True).
Common situations: User clicks delete on the Default channel; client offers a delete affordance on every channel without filtering is_default; bulk-delete loop includes the default id.
Related errors
- Cannot modify the default channel
- Channel with name '{data.name}' already exists
- Profile {profile_id} not found
- {exception message from create_channel (ValueError)}
- {exception message from update_channel (ValueError)}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/7e0939bc2c9d59c1.
Report an issue: GitHub.