jamiepine/voicebox · error · Error

HTTP ${res.status}

Error message

HTTP ${res.status}

What it means

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>`.

Source

Thrown at app/src/lib/api/client.ts:288

    });
  }

  async regenerateGeneration(generationId: string): Promise<GenerationResponse> {
    return this.request<GenerationResponse>(`/generate/${generationId}/regenerate`, {
      method: 'POST',
    });
  }

  async importAudio(file: File): Promise<GenerationResponse> {
    const form = new FormData();
    form.append('file', file);
    const res = await fetch(`${this.getBaseUrl()}/generate/import`, {
      method: 'POST',
      body: form,
    });
    if (!res.ok) {
      const detail = await res.text().catch(() => res.statusText);
      throw new Error(detail || `HTTP ${res.status}`);
    }
    return res.json();
  }

  async toggleFavorite(generationId: string): Promise<{ is_favorited: boolean }> {
    return this.request<{ is_favorited: boolean }>(`/history/${generationId}/favorite`, {
      method: 'POST',
    });
  }

  // History
  async listHistory(query?: HistoryQuery): Promise<HistoryListResponse> {
    const params = new URLSearchParams();
    if (query?.profile_id) params.append('profile_id', query.profile_id);
    if (query?.search) params.append('search', query.search);
    if (query?.limit) params.append('limit', query.limit.toString());
    if (query?.offset) params.append('offset', query.offset.toString());

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Read the raw text body the message carries — unlike other errors it is the literal server response, which usually names the unsupported codec.
  2. Convert the file to wav/mp3 client-side or ask the user to re-export before importing.
  3. Confirm ffmpeg is installed and on PATH in the backend environment.
  4. On 413, shrink the file; on 500 check backend logs for the decoder error.

Example fix

// before
const g = await api.importAudio(file);

// after — prefer widely-supported containers
const OK = ['audio/wav', 'audio/x-wav', 'audio/mpeg', 'audio/mp3'];
if (!OK.includes(file.type)) throw new Error('Import WAV or MP3 only');
const g = await api.importAudio(file);
Defensive patterns

Strategy: validation

Validate before calling

const OK_AUDIO = ['audio/wav', 'audio/x-wav', 'audio/mpeg', 'audio/mp3'];
function validateImportAudio(f: File) {
  if (!OK_AUDIO.includes(f.type)) throw new Error('Import WAV or MP3 only');
}

Type guard

function isImportableAudio(f: File): boolean {
  return ['audio/wav', 'audio/x-wav', 'audio/mpeg', 'audio/mp3'].includes(f.type);
}

Try / catch

try {
  validateImportAudio(file);
  return await api.importAudio(file);
} catch (e) {
  // importAudio surfaces raw text — show it directly
  toast.error(`Import failed: ${(e as Error).message}`);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/96de6cae7d116a73. Report an issue: GitHub.