jamiepine/voicebox · error · ValueError

Channel {channel_id} not found

Error message

Channel {channel_id} not found

What it means

Raised as ValueError (translated to HTTP 400 by PUT /channels/{channel_id}/voices) when no DBAudioChannel matches channel_id. set_channel_voices verifies the channel exists before touching any mapping rows. Note the route maps all ValueError to 400, so a missing channel is reported as 400 rather than the more conventional 404 — clients should treat it as 'not found'.

Source

Thrown at backend/services/channels.py:205

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_id
    ).all()
    return [m.profile_id for m in mappings]


async def set_channel_voices(
    channel_id: str,
    data: ChannelVoiceAssignment,
    db: Session,
) -> None:
    """Set which voices are assigned to a channel."""
    # Verify channel exists
    channel = db.query(DBAudioChannel).filter_by(id=channel_id).first()
    if not channel:
        raise ValueError(f"Channel {channel_id} not found")
    
    # Verify all profiles exist
    for profile_id in data.profile_ids:
        profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()
        if not profile:
            raise ValueError(f"Profile {profile_id} not found")
    
    # Delete existing mappings for this channel
    db.query(DBProfileChannelMapping).filter_by(channel_id=channel_id).delete()
    
    # Add new mappings
    for profile_id in data.profile_ids:
        mapping = DBProfileChannelMapping(
            profile_id=profile_id,
            channel_id=channel_id,
        )
        db.add(mapping)
    

View on GitHub (pinned to 51f49dea19)

Solutions

  1. GET /channels first and ensure channel_id is present before assigning voices.
  2. On 400 with 'Channel ... not found', invalidate the local channel and refresh.
  3. Upstream, consider routing missing-channel through 404 to disambiguate from a bad profile_ids value (also a 400).

Example fix

// before
await api.setChannelVoices(channelId, { profile_ids });

// after
const exists = (await api.listChannels()).some(c => c.id === channelId);
if (!exists) { refreshChannels(); return; }
await api.setChannelVoices(channelId, { profile_ids });
Defensive patterns

Strategy: validation

Validate before calling

async function assignVoicesToExistingChannel(api, channelId, profileIds) {
  const channels = await api.listChannels();
  if (!channels.some(c => c.id === channelId)) {
    throw new Error('channel not found');
  }
  return api.setChannelVoices(channelId, { profile_ids: profileIds });
}

Type guard

function isLiveChannelId(channels, id) {
  return Array.isArray(channels) && channels.some(c => c.id === id);
}

Try / catch

try { await api.setChannelVoices(channelId, { profile_ids }); }
catch (e) {
  // route returns 400 for a missing channel; treat as not-found
  if (e.status === 400 && /Channel .* not found/.test(e.detail)) {
    await refreshChannels(); notify('Channel no longer exists');
  } else throw e;
}

Prevention

When it happens

Trigger: PUT /channels/{channel_id}/voices with a channel_id that doesn't exist (deleted, typo, belongs to another scope). The profile_ids are not yet validated at this point, so this fires first.

Common situations: Stale channel_id in the client after deletion; voice-assignment UI opened on a channel that was removed elsewhere; copy/paste id error.

Related errors


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