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
- Convert the source to one of the allowed formats before uploading (wav/mp3/flac/ogg/m4a/aac/webm).
- 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.
- Validate the filename extension on the client before the upload to give immediate feedback.
- 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
- Set the file input `accept` attribute to the allowed list.
- Validate the suffix client-side before uploading.
- Keep the client allow-list in sync with IMPORT_AUDIO_EXTENSIONS.
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
- Empty audio file.
- File exceeds {IMPORT_AUDIO_MAX_BYTES // (1024 * 1024)} MB li
- Could not decode audio: {decode_err}
- HTTP ${res.status}
- File too large. Maximum size is {MAX_FILE_SIZE / (1024 * 102
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/c010b521044de1a6.
Report an issue: GitHub.