{"record":{"id":"83314d219379bc3b","repo":"jamiepine/voicebox","slug":"exception-message-from-update-channel-valueerror","errorCode":null,"errorMessage":"{exception message from update_channel (ValueError)}","messagePattern":"\\{exception message from update_channel \\(ValueError\\)\\}","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/routes/channels.py","lineNumber":56,"sourceCode":"    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,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Update an audio channel.\"\"\"\n    try:\n        channel = await channels.update_channel(channel_id, data, db)\n        if not channel:\n            raise HTTPException(status_code=404, detail=\"Channel not found\")\n        return channel\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n\n\n@router.delete(\"/channels/{channel_id}\")\nasync def delete_channel(\n    channel_id: str,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Delete an audio channel.\"\"\"\n    try:\n        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\")","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/channels.py#L38-L74","documentation":"HTTP 400 raised by PUT /channels/{channel_id} when channels.update_channel() raises ValueError. The service raises ValueError in exactly two business-rule cases: 'Cannot modify the default channel' (channel.is_default is true) and \"Channel with name '{name}' already exists\" (name collides with another row). The handler re-wraps the exception message verbatim into the 400 detail. It is a state/conflict error, not a not-found error.","triggerScenarios":"PUTting to a channel that is the default (is_default=true), or supplying a name that another non-default channel already uses.","commonSituations":"Attempting to rename a channel to a duplicate name; trying to edit the seeded default channel instead of a user-created one; UI not disabling the edit form for the default channel.","solutions":["If the message mentions 'default channel', pick a non-default channel or create a new one.","If the message is a duplicate-name error, choose a unique name or first rename the conflicting channel.","Disable edit controls in the UI for channels where is_default is true.","Surface the exact detail string to the user; it identifies the violated rule."],"exampleFix":"// before\nawait fetch(`/channels/${id}`, { method: 'PUT', body: JSON.stringify({ name: 'Main' }) });\n// after\nif (channel.is_default) { alert('Default channel cannot be edited'); return; }\nconst names = await fetch('/channels').then(r => r.json()).then(cs => cs.map(c => c.name));\nif (names.includes(newName)) { alert('Name already in use'); return; }\nawait fetch(`/channels/${id}`, { method: 'PUT', body: JSON.stringify({ name: newName }) });","handlingStrategy":"validation","validationCode":"if (channel.is_default) throw new Error('cannot modify default channel');\nconst names = (await fetch('/channels').then(r => r.json())).map(c => c.name);\nif (names.includes(data.name)) throw new Error('name already used');","typeGuard":"function isDefaultChannel(c): c is { is_default: true } { return c?.is_default === true; }","tryCatchPattern":"try {\n  const r = await fetch(`/channels/${id}`, { method:'PUT', body: JSON.stringify(data) });\n  if (r.status === 400) { const { detail } = await r.json(); showUser(detail); }\n} catch (e) { /* network */ }","preventionTips":["Never edit the default channel; create a new one instead.","Check name uniqueness against the live list before submitting.","Surface the server's detail string so the user knows which rule fired."],"tags":["channels","http","validation","conflict","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}