{"record":{"id":"46ffedc95ef14e53","repo":"jamiepine/voicebox","slug":"exception-message-from-get-channel-voices-valuee","errorCode":null,"errorMessage":"{exception message from get_channel_voices (ValueError)}","messagePattern":"\\{exception message from get_channel_voices \\(ValueError\\)\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/routes/channels.py","lineNumber":84,"sourceCode":"        success = await channels.delete_channel(channel_id, db)\n        if not success:\n            raise HTTPException(status_code=404, detail=\"Channel not found\")\n        return {\"message\": \"Channel deleted successfully\"}\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n\n\n@router.get(\"/channels/{channel_id}/voices\")\nasync def get_channel_voices(\n    channel_id: str,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Get list of profile IDs assigned to a channel.\"\"\"\n    try:\n        profile_ids = await channels.get_channel_voices(channel_id, db)\n        return {\"profile_ids\": profile_ids}\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n\n\n@router.put(\"/channels/{channel_id}/voices\")\nasync def set_channel_voices(\n    channel_id: str,\n    data: models.ChannelVoiceAssignment,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Set which voices are assigned to a channel.\"\"\"\n    try:\n        await channels.set_channel_voices(channel_id, data, db)\n        return {\"message\": \"Channel voices updated successfully\"}\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n","sourceCodeStart":66,"sourceCodeEnd":99,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/channels.py#L66-L99","documentation":"HTTP 400 branch in GET /channels/{channel_id}/voices that catches ValueError from channels.get_channel_voices(). In the current service implementation get_channel_voices() only queries the profile-channel mapping table and returns a list; it never raises ValueError. The catch is defensive scaffolding, so with the present code this 400 is effectively unreachable. A miss on the channel ID simply returns an empty profile_ids list (200), not a 404 and not this 400.","triggerScenarios":"Not reachable with the current service code; would only fire if a future change to get_channel_voices() adds a ValueError raise (e.g. validating the channel exists).","commonSituations":"Encountering this in logs would indicate the service contract changed; otherwise callers should not expect it.","solutions":["Do not write client code expecting a 400 here; rely on the 200 + empty list semantics today.","If you see this error, diff backend/services/channels.py for a new ValueError in get_channel_voices().","Treat an empty profile_ids result as 'channel has no voices or channel is absent'."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"const r = await fetch(`/channels/${id}/voices`);\nif (r.status === 400) { /* not produced by current service; check for code drift */ }","preventionTips":["Do not write client code expecting a 400 from this endpoint today.","Rely on 200 with possibly-empty profile_ids.","If a 400 appears, diff the service for a new ValueError raise."],"tags":["channels","http","unreachable","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}