jamiepine/voicebox · warning · HTTPException
Uploaded file is empty
Error message
Uploaded file is empty
What it means
Returned as a 400 from POST /captures when the uploaded audio file stream read zero bytes (the loop `while chunk := await file.read(UPLOAD_CHUNK_SIZE)` produced an empty concatenation). The route streams the upload into memory in chunks and explicitly rejects empty bodies before invoking STT, because a zero-length audio would fail downstream anyway. This is a client-side input error.
Source
Thrown at backend/routes/captures.py:39
UPLOAD_CHUNK_SIZE = 1024 * 1024 # 1 MB
@router.post("/captures", response_model=models.CaptureCreateResponse)
async def create_capture_endpoint(
file: UploadFile = File(...),
source: str = Form("file"),
language: str | None = Form(None),
stt_model: str | None = Form(None),
db: Session = Depends(get_db),
):
"""Upload audio, run STT, persist the capture."""
chunks = []
while chunk := await file.read(UPLOAD_CHUNK_SIZE):
chunks.append(chunk)
audio_bytes = b"".join(chunks)
if not audio_bytes:
raise HTTPException(status_code=400, detail="Uploaded file is empty")
saved = settings_service.get_capture_settings(db)
resolved_stt = stt_model or saved.stt_model
if language is None:
resolved_language = None if saved.language == "auto" else saved.language
else:
resolved_language = None if language == "auto" else language
try:
capture = await captures_service.create_capture(
audio_bytes=audio_bytes,
filename=file.filename or "capture.wav",
source=source,
language=resolved_language,
stt_model=resolved_stt,
db=db,
)
except ValueError as e:View on GitHub (pinned to 51f49dea19)
Solutions
- On the client, check the recorded Blob's size > 0 before appending to FormData and posting.
- Verify microphone permissions are granted so the recorder actually captures samples.
- Ensure the recorder's stop() is only called after audio data has arrived (min-duration guard).
- Log file.size and file.type on the client before upload to catch empty captures early.
Example fix
// before
const fd = new FormData();
fd.append('file', blob);
await fetch('/captures', { method: 'POST', body: fd });
// after
if (!blob || blob.size === 0) {
toast('No audio captured');
return;
}
const fd = new FormData();
fd.append('file', blob);
await fetch('/captures', { method: 'POST', body: fd }); Defensive patterns
Strategy: validation
Validate before calling
// Client-side guard before upload.
if (!blob || blob.size === 0) {
throw new Error('No audio recorded; refusing to upload empty file');
}
const fd = new FormData();
fd.append('file', blob, 'capture.wav'); Type guard
function isNonEmptyAudio(blob: Blob | null | undefined): blob is Blob {
return blob instanceof Blob && blob.size > 0;
} Try / catch
try {
const r = await fetch('/captures', { method: 'POST', body: fd });
if (r.status === 400) {
const body = await r.json();
if (body.detail === 'Uploaded file is empty') {
toast('Recording was empty; check mic access');
return;
}
throw new Error(body.detail);
}
} catch (e) { showNetworkError(e); } Prevention
- Reject empty recordings on the client before building FormData.
- Confirm microphone permission and that the recorder actually received data before stop().
- Log blob.size on the client to detect silent zero-length captures.
When it happens
Trigger: POST /captures with a multipart 'file' field that is empty, or a filename pointing to a 0-byte file, or a request where the file part was sent without a body (e.g. frontend sent FormData with an empty Blob).
Common situations: Recorder started and stopped with no audio captured (mic permission denied silently, or instant stop); the OS produced a 0-byte temp file; a bug in the client where an empty Blob is appended to FormData.
Related errors
- {exception message from wrapped ValueError}
- limit must be between 1 and 200
- offset must be >= 0
- {exception message from create_channel (ValueError)}
- Model ${model_size} is not downloaded yet. Use /generate to
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/15582537e4545325.
Report an issue: GitHub.