{"record":{"id":"96de6cae7d116a73","repo":"jamiepine/voicebox","slug":"http-res-status","errorCode":null,"errorMessage":"HTTP ${res.status}","messagePattern":"HTTP \\$\\{res\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/lib/api/client.ts","lineNumber":288,"sourceCode":"    });\n  }\n\n  async regenerateGeneration(generationId: string): Promise<GenerationResponse> {\n    return this.request<GenerationResponse>(`/generate/${generationId}/regenerate`, {\n      method: 'POST',\n    });\n  }\n\n  async importAudio(file: File): Promise<GenerationResponse> {\n    const form = new FormData();\n    form.append('file', file);\n    const res = await fetch(`${this.getBaseUrl()}/generate/import`, {\n      method: 'POST',\n      body: form,\n    });\n    if (!res.ok) {\n      const detail = await res.text().catch(() => res.statusText);\n      throw new Error(detail || `HTTP ${res.status}`);\n    }\n    return res.json();\n  }\n\n  async toggleFavorite(generationId: string): Promise<{ is_favorited: boolean }> {\n    return this.request<{ is_favorited: boolean }>(`/history/${generationId}/favorite`, {\n      method: 'POST',\n    });\n  }\n\n  // History\n  async listHistory(query?: HistoryQuery): Promise<HistoryListResponse> {\n    const params = new URLSearchParams();\n    if (query?.profile_id) params.append('profile_id', query.profile_id);\n    if (query?.search) params.append('search', query.search);\n    if (query?.limit) params.append('limit', query.limit.toString());\n    if (query?.offset) params.append('offset', query.offset.toString());\n","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/app/src/lib/api/client.ts#L270-L306","documentation":"Thrown by `importAudio(file)` (client.ts:279-291) when `POST /generate/import` (multipart audio) returns non-2xx. NOTE: this method diverges from the rest of the client — it reads the body as `text()` (not JSON) and its fallback message is `HTTP ${res.status}` (no `error!` wording, no `formatErrorDetail`). So the surfaced message is either the raw server text or a terse `HTTP <code>`.","triggerScenarios":"Audio codec/container not supported by the backend's ffmpeg/decoder pipeline; file too large; corrupt/truncated audio; backend cannot probe duration; backend model unavailable to attach the imported audio to a generation.","commonSituations":"User drags in an `.m4a`, `.ogg`, or unusual container the backend ffmpeg build lacks a codec for; partial upload; backend ffmpeg binary missing from the PATH so probing throws a 500.","solutions":["Read the raw text body the message carries — unlike other errors it is the literal server response, which usually names the unsupported codec.","Convert the file to wav/mp3 client-side or ask the user to re-export before importing.","Confirm ffmpeg is installed and on PATH in the backend environment.","On 413, shrink the file; on 500 check backend logs for the decoder error."],"exampleFix":"// before\nconst g = await api.importAudio(file);\n\n// after — prefer widely-supported containers\nconst OK = ['audio/wav', 'audio/x-wav', 'audio/mpeg', 'audio/mp3'];\nif (!OK.includes(file.type)) throw new Error('Import WAV or MP3 only');\nconst g = await api.importAudio(file);","handlingStrategy":"validation","validationCode":"const OK_AUDIO = ['audio/wav', 'audio/x-wav', 'audio/mpeg', 'audio/mp3'];\nfunction validateImportAudio(f: File) {\n  if (!OK_AUDIO.includes(f.type)) throw new Error('Import WAV or MP3 only');\n}","typeGuard":"function isImportableAudio(f: File): boolean {\n  return ['audio/wav', 'audio/x-wav', 'audio/mpeg', 'audio/mp3'].includes(f.type);\n}","tryCatchPattern":"try {\n  validateImportAudio(file);\n  return await api.importAudio(file);\n} catch (e) {\n  // importAudio surfaces raw text — show it directly\n  toast.error(`Import failed: ${(e as Error).message}`);\n}","preventionTips":["Note this endpoint returns raw text errors (not JSON) — surface the message verbatim.","Confirm ffmpeg is on the backend PATH for exotic codecs.","Pre-convert unusual containers to wav/mp3."],"tags":["http","file-upload","audio","import","voicebox"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}