{"record":{"id":"b9918a04be127a14","repo":"jamiepine/voicebox","slug":"sample-not-found","errorCode":null,"errorMessage":"Sample not found","messagePattern":"Sample not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"backend/routes/audio.py","lineNumber":77,"sourceCode":"            else \"Audio file not found\"\n        )\n        raise HTTPException(status_code=404, detail=detail)\n\n    return FileResponse(\n        audio_path,\n        media_type=_audio_media_type(audio_path),\n        filename=f\"generation_{generation_id}{audio_path.suffix}\",\n    )\n\n\n@router.get(\"/samples/{sample_id}\")\nasync def get_sample_audio(sample_id: str, db: Session = Depends(get_db)):\n    \"\"\"Serve profile sample audio file.\"\"\"\n    from ..database import ProfileSample as DBProfileSample\n\n    sample = db.query(DBProfileSample).filter_by(id=sample_id).first()\n    if not sample:\n        raise HTTPException(status_code=404, detail=\"Sample not found\")\n\n    audio_path = config.resolve_storage_path(sample.audio_path)\n    if audio_path is None or not audio_path.is_file():\n        raise HTTPException(status_code=404, detail=\"Audio file not found\")\n\n    return FileResponse(\n        audio_path,\n        media_type=\"audio/wav\",\n        filename=f\"sample_{sample_id}.wav\",\n    )\n","sourceCodeStart":59,"sourceCodeEnd":88,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/audio.py#L59-L88","documentation":"Returned as a 404 from GET /audio/samples/{sample_id} when no ProfileSample row matches the given id in the database. The lookup is a direct db.query(...).filter_by(id=sample_id).first(); any id not present (typo, wrong scope, deleted sample) yields None and triggers this. Pure not-found, no file or config involvement.","triggerScenarios":"GET /audio/samples/{sample_id} with an id that does not exist in the profile_samples table — e.g. an old/cached id from the client, a sample that was deleted, or an id from a different user/profile scope.","commonSituations":"Frontend holds a stale sample id after the user deleted/re-profiled; the id is copy-pasted wrong; test fixtures reference a sample that wasn't seeded.","solutions":["Confirm sample_id is the current id from the samples list endpoint, not a cached value.","Verify the ProfileSample row exists: SELECT * FROM profile_samples WHERE id = ?.","If the sample was deleted intentionally, refresh the client's sample list.","Add client-side handling for 404 on sample fetch to silently drop the reference."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Fetch the samples list first; only request audio for ids that appear in it.\nconst samples = await listSamples();\nconst valid = samples.some(s => s.id === sampleId);\nif (!valid) throw new Error('Sample id not in current list');","typeGuard":"function isKnownSample(sampleId: string, known: { id: string }[]): boolean {\n  return known.some(s => s.id === sampleId);\n}","tryCatchPattern":"try {\n  const r = await fetch(`/audio/samples/${sampleId}`);\n  if (r.status === 404) { dropStaleSample(sampleId); return; }\n  play(await r.blob());\n} catch (e) { showNetworkError(e); }","preventionTips":["Always source sample ids from the live samples list, never from cached state.","After deleting a sample, remove its id from the client immediately.","Treat 404 on sample fetch as 'remove reference' rather than an error."],"tags":["audio","sample","not-found","fastapi"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}