{"record":{"id":"7d1d2942a33ab596","repo":"jamiepine/voicebox","slug":"profile-not-found-7d1d29","errorCode":null,"errorMessage":"Profile not found","messagePattern":"Profile not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"backend/routes/profiles.py","lineNumber":117,"sourceCode":"                    \"voice_id\": speaker_id,\n                    \"name\": display_name,\n                    \"gender\": gender,\n                    \"language\": lang,\n                }\n                for speaker_id, display_name, gender, lang, _desc in QWEN_CUSTOM_VOICES\n            ],\n        }\n    return {\"engine\": engine, \"voices\": []}\n\n@router.get(\"/profiles/{profile_id}\", response_model=models.VoiceProfileResponse)\nasync def get_profile(\n    profile_id: str,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Get a voice profile by ID.\"\"\"\n    profile = await profiles.get_profile(profile_id, db)\n    if not profile:\n        raise HTTPException(status_code=404, detail=\"Profile not found\")\n    return profile\n\n\n@router.put(\"/profiles/{profile_id}\", response_model=models.VoiceProfileResponse)\nasync def update_profile(\n    profile_id: str,\n    data: models.VoiceProfileCreate,\n    db: Session = Depends(get_db),\n):\n    \"\"\"Update a voice profile.\"\"\"\n    try:\n        profile = await profiles.update_profile(profile_id, data, db)\n        if not profile:\n            raise HTTPException(status_code=404, detail=\"Profile not found\")\n        return profile\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/profiles.py#L99-L135","documentation":"GET /profiles/{profile_id} calls profiles.get_profile; if it returns a falsy value (None) the route raises HTTP 404 'Profile not found'. This is the standard missing-resource guard: the id did not match any row in the profiles table. The lookup is by the string profile_id column, so a typo, a deleted row, or an id from another environment all collapse into this one response.","triggerScenarios":"GET /profiles/{profile_id} with an id that was never created; an id of a profile deleted via DELETE /profiles/{id}; an id copy-pasted from a different deployment/database; a UUID with a stray leading/trailing space.","commonSituations":"Frontend cached a profile list, user deleted the profile in another tab, then the stale UI issues a GET; a bookmarked URL pointing at an old profile; passing the DB numeric PK instead of the string profile_id.","solutions":["Verify the id against the current list returned by GET /profiles.","On the client, treat a 404 here as 'evict this id from local cache and fall back to the list view'.","Confirm you are sending the string profile_id returned at create time, not an internal numeric primary key.","Trim whitespace from the path segment before sending."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Verify existence before the targeted GET\nasync def get_profile_or_evict(client, profile_id):\n    listing = (await client.get(\"/profiles\")).json()\n    ids = {p[\"id\"] for p in listing}\n    if profile_id not in ids:\n        return None            # caller should evict the stale id\n    return await client.get(f\"/profiles/{profile_id}\")","typeGuard":null,"tryCatchPattern":"resp = await client.get(f\"/profiles/{profile_id}\")\nif resp.status_code == 404:\n    evict_from_cache(profile_id)\n    show_default_view()\nelse:\n    resp.raise_for_status()","preventionTips":["Treat 404 here as authoritative and evict the id from all client caches.","Always source profile ids from a fresh GET /profiles rather than bookmarks.","Trim whitespace from ids before sending."],"tags":["fastapi","profile","not-found","rest"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}