jamiepine/voicebox · warning · HTTPException

Unsupported audio format '{suffix}'. Allowed: {sorted(IMPORT

Error message

Unsupported audio format '{suffix}'. Allowed: {sorted(IMPORT_AUDIO_EXTENSIONS)}

What it means

Returned by POST /generate/import when the uploaded file's extension is not in IMPORT_AUDIO_EXTENSIONS = {'.wav','.mp3','.flac','.ogg','.m4a','.aac','.webm'}. The check is purely on the filename suffix via `Path(file.filename or '').suffix.lower()`. HTTP 400. The response detail lists the allowed set sorted, so the client can present it.

Source

Thrown at backend/routes/generations.py:429

        _wav_stream(),
        media_type="audio/wav",
        headers={"Content-Disposition": 'attachment; filename="speech.wav"'},
    )


@router.post("/generate/import", response_model=models.GenerationResponse)
async def import_audio(
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
):
    """Register an external audio file as a generation row.

    Designed for the story timeline so users can drop in music or other
    non-TTS audio. The row points at a singleton "Imported Audio" profile
    so the existing generation/story plumbing keeps working unchanged."""
    suffix = Path(file.filename or "").suffix.lower()
    if suffix not in IMPORT_AUDIO_EXTENSIONS:
        raise HTTPException(
            status_code=400,
            detail=f"Unsupported audio format '{suffix}'. Allowed: {sorted(IMPORT_AUDIO_EXTENSIONS)}",
        )

    chunks: list[bytes] = []
    total = 0
    while True:
        chunk = await file.read(1024 * 1024)
        if not chunk:
            break
        total += len(chunk)
        if total > IMPORT_AUDIO_MAX_BYTES:
            raise HTTPException(
                status_code=413,
                detail=f"File exceeds {IMPORT_AUDIO_MAX_BYTES // (1024 * 1024)} MB limit.",
            )
        chunks.append(chunk)
    audio_bytes = b"".join(chunks)

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Convert the source to one of the allowed formats before uploading (wav/mp3/flac/ogg/m4a/aac/webm).
  2. If a common format like .opus is needed, add it to IMPORT_AUDIO_EXTENSIONS in backend/routes/generations.py and ensure load_audio (the decoder downstream) supports it.
  3. Validate the filename extension on the client before the upload to give immediate feedback.
  4. Handle the 400 response by surfacing the returned allowed-formats list to the user.

Example fix

// before
<input type="file" />

// after
const ALLOWED = ['.wav','.mp3','.flac','.ogg','.m4a','.aac','.webm'];
<input type="file" accept={ALLOWED.join(',')} onChange={e => {
  if (!ALLOWED.some(ext => e.target.files[0].name.toLowerCase().endsWith(ext))) {
    alert('Unsupported format'); return;
  }
  upload(e.target.files[0]);
}} />
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = ['.wav','.mp3','.flac','.ogg','.m4a','.aac','.webm'];
function allowedExt(name) {
  return ALLOWED.includes(name.slice(name.lastIndexOf('.')).toLowerCase());
}
if (!allowedExt(file.name)) { alert('Unsupported format'); return; }

Type guard

const IMPORT_AUDIO_EXTENSIONS = ['.wav','.mp3','.flac','.ogg','.m4a','.aac','.webm'];
function hasImportableAudioExt(filename) {
  const dot = filename.lastIndexOf('.');
  return dot >= 0 && IMPORT_AUDIO_EXTENSIONS.includes(filename.slice(dot).toLowerCase());
}

Try / catch

try {
  const res = await fetch('/generate/import', { method:'POST', body: form });
  if (res.status === 400) {
    const detail = await res.json();
    showFormatError(detail.detail); // includes allowed list
    return;
  }
} catch (e) { console.error(e); }

Prevention

When it happens

Trigger: Uploading a file with an extension outside the allow-list (e.g. .opus, .aiff, .mp4, .txt); uploading a file with no extension (filename == '' or no dot); uploading with an uppercase extension that isn't lowered (note: suffix IS lowercased, so this is fine, but a truly unsupported extension still fails).

Common situations: User drags a .mp4 video or .opus clip into the story timeline import; macOS hidden-file upload with no extension; a renamed file whose new extension isn't whitelisted.

Related errors


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