{"record":{"id":"d979e62f2912b34f","repo":"jamiepine/voicebox","slug":"str-e-d979e6","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"backend/routes/transcription.py","lineNumber":101,"sourceCode":"                status_code=202,\n                detail={\n                    \"message\": f\"Whisper model {model_size} is being downloaded. Please wait and try again.\",\n                    \"model_name\": progress_model_name,\n                    \"downloading\": True,\n                },\n            )\n\n        text = await whisper_model.transcribe(stt_path, language, model_size)\n\n        return models.TranscriptionResponse(\n            text=text,\n            duration=duration,\n        )\n\n    except HTTPException:\n        raise\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n    finally:\n        Path(tmp_path).unlink(missing_ok=True)\n        if stt_path != tmp_path:\n            Path(stt_path).unlink(missing_ok=True)\n","sourceCodeStart":83,"sourceCodeEnd":106,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/transcription.py#L83-L106","documentation":"Raised (HTTP 500) by POST /transcribe as the catch-all for any non-HTTPException during transcription (genuine HTTPExceptions, including the 202 'model downloading', are re-raised first). The detail is raw str(e). The body of the handler loads/decodes audio, optionally re-encodes to WAV, and calls whisper_model.transcribe — so the underlying cause is typically a decode failure, a Whisper runtime error, or a model-loading problem that wasn't caught by the is_loaded/_is_model_cached guards.","triggerScenarios":"Uploaded file is zero-length or an unreadable container after the upload read; librosa load succeeds but the re-encoded .stt.wav is malformed; whisper_model.transcribe throws (OOM, unsupported sample rate, incomplete model download despite the cache check).","commonSituations":"Browser recording truncated/empty; a partially downloaded Whisper model passes _is_model_cached but fails at inference; GPU/MPS OOM on a long file; ffmpeg/audioread backend missing on the host so decode of exotic containers fails inside transcribe.","solutions":["Read the server traceback — str(e) is only the surface; the logged exception has the frame.","Validate the upload is non-empty before processing (the route reads in 1MB chunks but doesn't reject empty input).","Ensure the Whisper model for the chosen size is fully downloaded (let the 202 download flow finish before retrying).","Replace detail=str(e) with a generic message and log server-side to avoid leaking internals.","Confirm ffmpeg is installed if inputs include webm/opus/m4a."],"exampleFix":"# before\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n\n# after\n    except Exception:\n        logger.exception(\"transcribe failed for %s\", tmp_path)\n        raise HTTPException(status_code=500, detail=\"Transcription failed; see server logs\")","handlingStrategy":"try-catch","validationCode":"function looksLikeUsableAudio(file) {\n  return file && file.size > 1024 && /\\.(wav|mp3|m4a|ogg|flac|aac|webm|opus)$/i.test(file.name || '');\n}\nif (!looksLikeUsableAudio(file)) { notify('Audio file is empty or unsupported'); return; }","typeGuard":"function isNonEmptyAudioFile(file) {\n  return file instanceof File && file.size > 0 && Boolean(file.name);\n}","tryCatchPattern":"try { await api.transcribe(file, language, model); }\ncatch (e) {\n  if (e.status === 500) { notify('Transcription failed; check server logs and audio file'); }\n  else if (e.status === 202) { notify('Model downloading; retry shortly'); }\n  else throw e;\n}","preventionTips":["Reject empty/undersized uploads before sending.","Ensure ffmpeg is installed so exotic containers decode server-side.","Let the 202 'downloading' flow finish before retrying a model that isn't cached.","Server-side, log the traceback and return a generic message instead of str(e)."],"tags":["api","transcription","whisper","error-handling","information-disclosure","audio"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}