jamiepine/voicebox · warning · ValueError

Channel with name '{data.name}' already exists

Error message

Channel with name '{data.name}' already exists

What it means

Raised as ValueError (HTTP 400) by create_channel when a DBAudioChannel row already exists with the requested name. The check is an exact, case-sensitive filter_by(name=data.name).first() before insert. New channels are always created with is_default=False, so name uniqueness against the default channel is also enforced here.

Source

Thrown at backend/services/channels.py:77

    
    return AudioChannelResponse(
        id=channel.id,
        name=channel.name,
        is_default=channel.is_default,
        device_ids=device_ids,
        created_at=channel.created_at,
    )


async def create_channel(
    data: AudioChannelCreate,
    db: Session,
) -> AudioChannelResponse:
    """Create a new audio channel."""
    # Check if name already exists
    existing = db.query(DBAudioChannel).filter_by(name=data.name).first()
    if existing:
        raise ValueError(f"Channel with name '{data.name}' already exists")
    
    # Create channel
    channel = DBAudioChannel(
        id=str(uuid.uuid4()),
        name=data.name,
        is_default=False,
        created_at=datetime.utcnow(),
    )
    db.add(channel)
    db.flush()
    
    # Add device mappings
    for device_id in data.device_ids:
        mapping = DBChannelDeviceMapping(
            id=str(uuid.uuid4()),
            channel_id=channel.id,
            device_id=device_id,
        )

View on GitHub (pinned to 51f49dea19)

Solutions

  1. GET /channels first and block names already in use (the check is case-sensitive, match exactly).
  2. On 400 with this message, refresh the channel list and let the user pick a new name.
  3. Add a UNIQUE constraint on name at the DB level as a backstop against the race, and map IntegrityError to the same 400.

Example fix

// before
await api.createChannel({ name: userInput, device_ids: [] });

// after
const taken = (await api.listChannels()).map(c => c.name);
if (taken.includes(userInput)) {
  setError('Name already in use'); return;
}
await api.createChannel({ name: userInput, device_ids: [] });
Defensive patterns

Strategy: validation

Validate before calling

function uniqueChannelName(existing, name) {
  const taken = new Set(existing.map(c => c.name));
  return name != null && name.length > 0 && !taken.has(name);
}
if (!uniqueChannelName(channels, draft.name)) { setError('Name already in use'); return; }

Type guard

function isAvailableChannelName(existing, name) {
  return typeof name === 'string' && name.trim().length > 0
    && !existing.some(c => c.name === name);
}

Try / catch

try { await api.createChannel(payload); }
catch (e) {
  if (e.status === 400 && /already exists/.test(e.detail)) {
    await refreshChannels(); notify('Name already in use');
  } else throw e;
}

Prevention

When it happens

Trigger: POST /channels with a name that already names another channel (including the default channel). Two concurrent POSTs racing to create the same name can both pass the check then the second flush/commit fails — though the explicit check usually catches it.

Common situations: User types a duplicate channel name; client retries a 'failed' create after the row was actually written; default channel is named 'Default' and the user tries to create 'Default'.

Related errors


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