{"record":{"id":"f9dc9cf2381073ce","repo":"jamiepine/voicebox","slug":"exception-message-from-create-channel-valueerror","errorCode":null,"errorMessage":"{exception message from create_channel (ValueError)}","messagePattern":"\\{exception message from create_channel \\(ValueError\\)\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/routes/channels.py","lineNumber":28,"sourceCode":"router = APIRouter()\n\n\n@router.get(\"/channels\", response_model=list[models.AudioChannelResponse])\nasync def list_channels(db: Session = Depends(get_db)):\n    \"\"\"List all audio channels.\"\"\"\n    return await channels.list_channels(db)\n\n\n@router.post(\"/channels\", response_model=models.AudioChannelResponse)\nasync def create_channel(\n    data: models.AudioChannelCreate,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Create a new audio channel.\"\"\"\n    try:\n        return await channels.create_channel(data, db)\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n\n\n@router.get(\"/channels/{channel_id}\", response_model=models.AudioChannelResponse)\nasync def get_channel(\n    channel_id: str,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Get an audio channel by ID.\"\"\"\n    channel = await channels.get_channel(channel_id, db)\n    if not channel:\n        raise HTTPException(status_code=404, detail=\"Channel not found\")\n    return channel\n\n\n@router.put(\"/channels/{channel_id}\", response_model=models.AudioChannelResponse)\nasync def update_channel(\n    channel_id: str,\n    data: models.AudioChannelUpdate,","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/channels.py#L10-L46","documentation":"Returned as a 400 from POST /channels wrapping any ValueError raised by channels.create_channel. The service uses ValueError for expected validation failures (duplicate channel name, invalid device/source config, out-of-range parameters) so they surface as client errors rather than 500s. The detail is str(e) — the service's specific message.","triggerScenarios":"POST /channels with a body that passes Pydantic validation but fails business-rule validation inside create_channel — e.g. a channel name that already exists, an audio device id that is not present on the system, an invalid source binding, or a parameter outside an allowed range.","commonSituations":"Creating a channel with a duplicate name; referencing an audio input device that was disconnected; a source value that conflicts with an existing channel; feeding a config that the service's deeper checks reject.","solutions":["Read the detail string — it names the exact business-rule violation.","For duplicate-name errors, choose a unique name or first delete/rename the conflicting channel.","For device errors, list available audio devices and use a valid id.","Move repeated business rules into the Pydantic model validators so they fail at parse time with structured errors."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-check business rules the service enforces.\nconst existing = await listChannels();\nif (existing.some(c => c.name === data.name)) {\n  throw new Error('Channel name already exists');\n}","typeGuard":"function isUniqueChannelName(name: string, existing: { name: string }[]): boolean {\n  return !existing.some(c => c.name === name);\n}","tryCatchPattern":"try {\n  const r = await fetch('/channels', { method: 'POST', body: JSON.stringify(data) });\n  if (r.status === 400) {\n    const { detail } = await r.json();\n    showUserFacingError(detail); // names the business-rule violation\n    return;\n  }\n} catch (e) { showNetworkError(e); }","preventionTips":["Fetch existing channels and check for name collisions before creating.","Validate audio device ids against the system's device list before submitting.","Push repeated business rules into Pydantic validators so they fail with structured errors at parse time."],"tags":["channel","validation","input","business-rule","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}