huggingface/transformers · error · HTTPException
Expected file upload, got string
Error message
Expected file upload, got string
What it means
HTTP 422 from the transcription handler when the multipart part named 'file' is a plain string instead of an UploadFile. This happens when the client sends 'file' as an ordinary form field (text value) rather than as a file part with a filename, so form['file'] is a str and await file_field.read() would crash.
Source
Thrown at src/transformers/cli/serving/transcription.py:108
``file`` (audio bytes), ``model`` (model ID), and optional ``stream`` flag.
Returns:
`JSONResponse | StreamingResponse`: Transcription result or SSE stream.
"""
from transformers.utils.import_utils import is_librosa_available, is_multipart_available
if not is_librosa_available():
raise ImportError("Missing librosa dependency for audio transcription. Install with `pip install librosa`")
if not is_multipart_available():
raise ImportError(
"Missing python-multipart dependency for file uploads. Install with `pip install python-multipart`"
)
async with request.form() as form:
self._validate_request(set(form.keys()))
file_field = form["file"]
if isinstance(file_field, str):
raise HTTPException(status_code=422, detail="Expected file upload, got string")
file_bytes = await file_field.read()
model = form["model"]
if not isinstance(model, str):
raise HTTPException(status_code=422, detail="Expected model name as string")
stream = str(form.get("stream", "false")).lower() == "true"
model_id_and_revision = self.model_manager.process_model_name(model)
audio_model, audio_processor = self.model_manager.load_model_and_processor(model_id_and_revision)
base_manager = self.generation_state.get_manager(model_id_and_revision)
if not isinstance(base_manager, GenerateManager):
raise HTTPException(status_code=400, detail="Audio transcription requires sequential generation (not CB)")
gen_manager = base_manager
audio_inputs = self._prepare_audio_inputs(file_bytes, audio_processor, audio_model)
if stream:
return self._streaming(gen_manager, audio_model, audio_processor, audio_inputs)
return await self._non_streaming(gen_manager, audio_model, audio_processor, audio_inputs)
View on GitHub (pinned to a597f97485)
Solutions
- Send the audio as a real file part: requests.post(url, files={'file': open('a.wav','rb')}, data={'model': 'whisper'})
- In JS, append a Blob/File to FormData, not a string: form.append('file', fileInput.files[0])
- If building multipart manually, include filename in the Content-Disposition so the parser creates an UploadFile
Example fix
# before
requests.post(url, data={'file': 'audio.wav', 'model': 'whisper'})
# after
requests.post(url, files={'file': open('audio.wav', 'rb')}, data={'model': 'whisper'}) Defensive patterns
Strategy: type-guard
Validate before calling
path = Path('audio.wav')
assert path.is_file(), f'{path} must be a real file'
with path.open('rb') as fh:
resp = requests.post(url, files={'file': (path.name, fh, 'audio/wav')}, data={'model': model_id}) Type guard
def is_upload_file(v) -> bool:
# a proper file part is (filename, binary stream, content type)
return isinstance(v, tuple) and isinstance(v[0], str) and hasattr(v[1], 'read') Try / catch
if resp.status_code == 422 and 'Expected file upload' in resp.text:
raise ValueError("send audio via files=, not data= (form fields arrive as strings)") from None Prevention
- Always open audio in binary mode and pass it through the files= argument
- In JS, append File/Blob objects to FormData, never path strings
When it happens
Trigger: Sending the audio as a form field (data={'file': '/path/to/audio.mp3'}) instead of a file part (files={'file': open(...)}); hand-rolled multipart bodies that omit the filename="..." disposition, which Starlette treats as text.
Common situations: Using requests with data= instead of files=; passing a base64 string or a path string rather than an opened binary file; frontend FormData appending a string instead of a Blob/File.
Related errors
- 'input' must be a string or list
- Unsupported input item type: {item_type!r}
- Missing `model` field in the request body.
- Unexpected fields in the request: {unexpected}
- Expected model name as string
AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14).
Data as JSON: /api/errors/2a8ba4cbcf5d1048.
Report an issue: GitHub.