{"record":{"id":"ee415c5df967bd1f","repo":"jamiepine/voicebox","slug":"http-res-status-ee415c","errorCode":null,"errorMessage":"HTTP ${res.status}","messagePattern":"HTTP \\$\\{res\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/components/CapturesTab/CapturesTab.tsx","lineNumber":345,"sourceCode":"\n  const exportToastError = (err: unknown) => {\n    toast({\n      title: t('captures.toast.exportFailed'),\n      description: err instanceof Error ? err.message : String(err),\n      variant: 'destructive',\n    });\n  };\n\n  const handleExportAudio = async () => {\n    if (!selected) return;\n    try {\n      const dest = await save({\n        defaultPath: `capture_${selected.id.slice(0, 8)}.wav`,\n        filters: [{ name: 'Audio', extensions: ['wav'] }],\n      });\n      if (!dest) return;\n      const res = await fetch(apiClient.getCaptureAudioUrl(selected.id));\n      if (!res.ok) throw new Error(`HTTP ${res.status}`);\n      const buf = new Uint8Array(await res.arrayBuffer());\n      await writeFile(dest, buf);\n      exportToastSuccess(dest);\n    } catch (err) {\n      exportToastError(err);\n    }\n  };\n\n  const handleExportTranscript = async () => {\n    if (!selected) return;\n    const text = (selected.transcript_refined || selected.transcript_raw || '').trim();\n    if (!text) {\n      toast({ title: t('captures.toast.exportEmpty'), variant: 'destructive' });\n      return;\n    }\n    try {\n      const dest = await save({\n        defaultPath: `capture_${selected.id.slice(0, 8)}.txt`,","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/app/src/components/CapturesTab/CapturesTab.tsx#L327-L363","documentation":"Thrown by handleExportAudio() after fetching a capture's audio bytes from apiClient.getCaptureAudioUrl(selected.id). If the response is not ok, it throws a generic HTTP <status>. The function is wrapped in try/catch that routes failures to exportToastError(), so the user sees an error toast.","triggerScenarios":"GET on the capture-audio URL returns 404 (audio file missing on disk / never persisted), 500 (backend error reading the file), 401/403 (auth/session expired), or a gateway 502/504. Also fails if the backend is unreachable.","commonSituations":"Captures older than the audio retention window had their WAV pruned. The backend storage volume was wiped/moved. The capture recorded but audio write failed silently (transcript exists, audio does not).","solutions":["Verify the capture has audio before enabling Export Audio (check a has_audio / audio_url field on CaptureResponse).","Differentiate 404 (show 'audio no longer available') from other statuses in the toast.","Confirm the backend capture-audio route and on-disk path are intact; check backend logs for the matching read error.","Ensure the auth token is still valid (re-auth on 401)."],"exampleFix":"// before\nconst res = await fetch(apiClient.getCaptureAudioUrl(selected.id));\nif (!res.ok) throw new Error(`HTTP ${res.status}`);\n// after\nconst res = await fetch(apiClient.getCaptureAudioUrl(selected.id));\nif (!res.ok) {\n  throw new Error(res.status === 404 ? 'Audio for this capture is no longer available.' : `HTTP ${res.status}`);\n}","handlingStrategy":"try-catch","validationCode":"function captureHasAudio(c: CaptureResponse): boolean {\n  return Boolean(c.audio_url || c.has_audio);\n}\n// Gate export:\nif (!captureHasAudio(selected)) return;","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(apiClient.getCaptureAudioUrl(selected.id));\n  if (!res.ok) throw new Error(res.status === 404 ? 'Audio no longer available.' : `HTTP ${res.status}`);\n  await writeFile(dest, new Uint8Array(await res.arrayBuffer()));\n  exportToastSuccess(dest);\n} catch (err) {\n  exportToastError(err);\n}","preventionTips":["Disable Export Audio when the capture has no audio_url/has_audio flag.","Differentiate 404 from other statuses in the user message.","Re-auth on 401 rather than showing a generic HTTP error."],"tags":["network","http","captures","export","react","typescript","tauri"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}