jamiepine/voicebox · warning · ValueError

Cannot modify the default channel

Error message

Cannot modify the default channel

What it means

Raised as ValueError (HTTP 400) by update_channel when the loaded channel has is_default=True. The default channel is immutable by design — its name and device mappings cannot be changed through update_channel. The guard fires after the channel is fetched but before any field mutation, so no partial write occurs.

Source

Thrown at backend/services/channels.py:121

        name=channel.name,
        is_default=channel.is_default,
        device_ids=data.device_ids,
        created_at=channel.created_at,
    )


async def update_channel(
    channel_id: str,
    data: AudioChannelUpdate,
    db: Session,
) -> Optional[AudioChannelResponse]:
    """Update an audio channel."""
    channel = db.query(DBAudioChannel).filter_by(id=channel_id).first()
    if not channel:
        return None
    
    if channel.is_default:
        raise ValueError("Cannot modify the default channel")
    
    # Update name if provided
    if data.name is not None:
        # Check if name already exists (excluding current channel)
        existing = db.query(DBAudioChannel).filter(
            DBAudioChannel.name == data.name,
            DBAudioChannel.id != channel_id
        ).first()
        if existing:
            raise ValueError(f"Channel with name '{data.name}' already exists")
        channel.name = data.name
    
    # Update device mappings if provided
    if data.device_ids is not None:
        # Delete existing mappings
        db.query(DBChannelDeviceMapping).filter_by(channel_id=channel_id).delete()
        
        # Add new mappings

View on GitHub (pinned to 51f49dea19)

Solutions

  1. In the UI, disable editing for the channel with is_default=true (it's returned in AudioChannelResponse).
  2. Route all desired device assignments through a non-default channel; create one if needed.
  3. If you genuinely need to alter the default channel's devices, do so via direct DB seeding/migration, not this endpoint.

Example fix

// before
if (selected) await api.updateChannel(selected.id, patch);

// after
if (selected && !selected.is_default) {
  await api.updateChannel(selected.id, patch);
} else {
  notify('The default channel cannot be modified');
}
Defensive patterns

Strategy: validation

Validate before calling

function isEditableChannel(ch) {
  return Boolean(ch) && ch.is_default === false;
}
if (!isEditableChannel(channel)) { notify('The default channel cannot be modified'); return; }

Type guard

function isMutableChannel(ch) {
  return Boolean(ch) && ch.is_default === false;
}

Try / catch

try { await api.updateChannel(id, patch); }
catch (e) {
  if (e.status === 400 && /default channel/.test(e.detail)) notify('Default channel is read-only');
  else throw e;
}

Prevention

When it happens

Trigger: PUT /channels/{channel_id} targeting the channel whose is_default flag is True — typically the seeded 'Default' channel. Includes attempts that only change device_ids (the guard is unconditional on is_default, not gated on whether name is being changed).

Common situations: User selects the Default channel in an edit UI and saves; client auto-saves device-mapping changes without checking is_default; trying to rename the default channel.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/77e53e2ee9ea9223. Report an issue: GitHub.