windmill-labs/windmill · warning · std::io::Error
err (multipart field stream error converted to io::Error dur
Error message
err (multipart field stream error converted to io::Error during file upload)
What it means
In `process_multipart`, an uploaded file's multipart field is consumed as a byte stream and piped into object storage. Any error yielded by the multipart field stream (malformed part, client disconnect mid-upload, decoder failure) is mapped into a `std::io::Error` of kind `Other` carrying the original error's string, and that io error then fails the upload request. The original reqwest/axum multipart error type is erased by the wrapping.
Source
Thrown at backend/windmill-api/src/args.rs:184
let file_key = get_random_file_name(ext);
let options = Attributes::from_iter(vec![
(Attribute::ContentType, content_type),
(
Attribute::ContentDisposition,
if let Some(filename) = filename {
format!("inline; filename=\"{}\"", filename)
} else {
"inline".to_string()
},
),
])
.into();
let bytes_stream = field
.into_stream()
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err));
// file_key is always freshly random here, so this never
// overwrites an existing object; the full size is the delta.
#[cfg(not(feature = "enterprise"))]
let max_size =
Some(ce_storage_quota_remaining(db, w_id, None).await? as usize);
#[cfg(feature = "enterprise")]
let max_size: Option<usize> = None;
match upload_file_internal(
s3_client.clone(),
&file_key,
bytes_stream,
options,
max_size,
)
.await
{View on GitHub (pinned to e474e8803c)
Solutions
- Retry the upload from the client once the connection is stable; the error is usually a transient transport failure
- Read the wrapped message inside the io error for the underlying multipart failure cause
- Check intermediary proxies/timeouts for large uploads and raise body size/timeout limits
- Test the upload with a standard client (curl/browser) to rule out malformed multipart encoding
Defensive patterns
Strategy: retry
Try / catch
try {
await uploadMultipart(file);
} catch (e) {
if (e instanceof Error && /multipart|stream/i.test(e.message)) {
// transient transport failure — retry upload from the start
}
} Prevention
- Use stable connections for large uploads; avoid flaky networks or raise timeouts
- Set adequate body-size limits on proxies between client and server
- Use standard HTTP clients to guarantee well-formed multipart bodies
- Handle browser cancellations gracefully client-side
When it happens
Trigger: A client aborts or resets the connection while streaming a file upload, the multipart body is truncated or malformed, or the field decoder errors partway through `field.into_stream()`.
Common situations: Large file uploads over flaky networks, browser cancellations, proxies cutting long uploads, malformed multipart boundaries from custom clients.
Related errors
- e.to_string() (Bedrock stream receive error wrapped in io::E
- No response body for SSE stream
- AI response was empty
- SSE error: ${previewJobUpdates}
- fdopen is not supported
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/a41d8d8d5ada7d4f.
Report an issue: GitHub.