jamiepine/voicebox · error · HTTPException
{exception message from set_channel_voices (ValueError)}
Error message
{exception message from set_channel_voices (ValueError)} What it means
HTTP 400 raised by PUT /channels/{channel_id}/voices when channels.set_channel_voices() raises ValueError. The service raises ValueError for two cases: 'Channel {channel_id} not found' (the channel row does not exist) and 'Profile {profile_id} not found' (one of the supplied profile_ids has no VoiceProfile row). The handler re-wraps the message into 400.
Source
Thrown at backend/routes/channels.py:98
try:
profile_ids = await channels.get_channel_voices(channel_id, db)
return {"profile_ids": profile_ids}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.put("/channels/{channel_id}/voices")
async def set_channel_voices(
channel_id: str,
data: models.ChannelVoiceAssignment,
db: Session = Depends(get_db),
):
"""Set which voices are assigned to a channel."""
try:
await channels.set_channel_voices(channel_id, data, db)
return {"message": "Channel voices updated successfully"}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
View on GitHub (pinned to 51f49dea19)
Solutions
- GET /channels/{channel_id} to confirm the channel exists before assigning voices.
- Validate every profile_id against the current profiles list before submitting.
- Parse the detail string: 'Channel ... not found' vs 'Profile ... not found' tells you which side is stale.
Example fix
// before
await fetch(`/channels/${cid}/voices`, { method:'PUT', body: JSON.stringify({ profile_ids: picked }) });
// after
const profiles = await fetch('/profiles').then(r => r.json());
const valid = picked.filter(id => profiles.some(p => p.id === id));
if (valid.length !== picked.length) { alert('One or more profiles no longer exist'); return; }
await fetch(`/channels/${cid}/voices`, { method:'PUT', body: JSON.stringify({ profile_ids: valid }) }); Defensive patterns
Strategy: validation
Validate before calling
const [chan, profiles] = await Promise.all([
fetch(`/channels/${cid}`).then(r => r.ok),
fetch('/profiles').then(r => r.json()),
]);
if (!chan) throw new Error('channel missing');
if (!profileIds.every(id => profiles.some(p => p.id === id))) throw new Error('stale profile id'); Type guard
function areValidProfileIds(ids, profiles): ids is string[] {
return ids.every(id => profiles.some(p => p.id === id));
} Try / catch
const r = await fetch(`/channels/${cid}/voices`, { method:'PUT', body: JSON.stringify({ profile_ids }) });
if (r.status === 400) { const { detail } = await r.json(); /* 'Channel .. not found' or 'Profile .. not found' */ } Prevention
- Confirm the channel exists before assigning voices.
- Filter profile_ids against the current profiles list before submit.
- Parse the detail to identify which side is stale.
When it happens
Trigger: PUT /channels/{channel_id}/voices with a channel_id that does not exist, or with a profile_ids array containing a profile ID that does not exist.
Common situations: Assigning a voice profile that was just deleted; pasting a profile_id from another resource; channel removed between page load and save.
Related errors
- Channel not found
- {exception message from update_channel (ValueError)}
- {exception message from delete_channel (ValueError)}
- Story item not found or invalid trim values
- {exception message from get_channel_voices (ValueError)}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/aa0ec1e2756e3381.
Report an issue: GitHub.