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
- GET /channels first and block names already in use (the check is case-sensitive, match exactly).
- On 400 with this message, refresh the channel list and let the user pick a new name.
- 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
- Fetch /channels and check name availability before submit.
- Make the name input case-sensitively unique against existing names.
- Add a DB UNIQUE constraint on name as a race-condition backstop.
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
- {exception message from update_channel (ValueError)}
- {exception message from delete_channel (ValueError)}
- {exception message from set_channel_voices (ValueError)}
- Story item not found or invalid split point
- Story item or version not found
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/ed0f73333eeacb89.
Report an issue: GitHub.