argoproj/argo-workflows · warning
Failed to parse multipart form:
Error message
Failed to parse multipart form:
What it means
r.ParseMultipartForm(32<<20) buffers up to 32MB in memory and spills the rest to disk; failures that are NOT MaxBytesError (e.g. malformed multipart body, wrong Content-Type, truncated stream) return 400 with 'Failed to parse multipart form: <detail>'. The message in this error index shows the prefix that gets the underlying error appended.
Source
Thrown at server/artifacts/artifact_server.go:168
}
r.Body = http.MaxBytesReader(w, r.Body, int64(maxUploadBytes))
// mime/multipart.ReadForm already removes temp files on parse error, but
// registering cleanup here makes the handler's correctness independent of
// that stdlib internal — any future error return still frees temp files.
defer func() {
if r.MultipartForm != nil {
_ = r.MultipartForm.RemoveAll()
}
}()
// Parse multipart form (max 32MB in memory, rest on disk)
if parseErr := r.ParseMultipartForm(32 << 20); parseErr != nil {
if _, ok := errors.AsType[*http.MaxBytesError](parseErr); ok {
http.Error(w, http.StatusText(http.StatusRequestEntityTooLarge), http.StatusRequestEntityTooLarge)
return
}
http.Error(w, "Failed to parse multipart form: "+parseErr.Error(), http.StatusBadRequest)
return
}
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "Failed to get file from form: "+err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
a.logger.WithFields(logging.Fields{
"filename": header.Filename,
"size": header.Size,
}).Info(ctx, "Received file for upload")
// Find the artifact in the WorkflowTemplate's arguments.artifacts
var templateArtifact *wfv1.Artifact
if wfTemplate.Spec.Arguments.Artifacts != nil {View on GitHub (pinned to 35bff19146)
Solutions
- Send a proper multipart/form-data body: curl -F 'file=@path' (not -d/--data-binary)
- Ensure the Content-Type header includes the multipart boundary (let the HTTP client set it automatically)
- Retry the upload if the connection was interrupted/truncated
- Check for intermediaries (ingress, service mesh) modifying or buffering the request body
Example fix
# before (raw body, not multipart) curl -X POST --data-binary @data.bin https://.../upload-artifacts/ns/tmpl/art # after curl -X POST -F 'file=@data.bin' https://.../upload-artifacts/ns/tmpl/art
Defensive patterns
Strategy: validation
Validate before calling
ct := req.Header.Get("Content-Type")
if !strings.HasPrefix(ct, "multipart/form-data") {
return fmt.Errorf("must POST multipart/form-data with a 'file' field, got %s", ct)
} Prevention
- Use -F/--form with curl or the HTTP client's multipart writer; never --data-binary
- Let the HTTP client set Content-Type so the boundary is included
- Retry on network interruption to avoid truncated bodies
- Verify proxies/mesh do not re-encode request bodies
When it happens
Trigger: Posting a body with Content-Type other than multipart/form-data (e.g. application/octet-stream, raw binary); corrupted or truncated multipart stream; missing boundary in Content-Type header; proxies that alter the body.
Common situations: Clients using -d or --data-binary instead of -F with curl; HTTP libraries posting fields without multipart encoding; request aborted mid-upload leaving an incomplete body; ingress gateways buffering incorrectly.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid path. Expected: /upload-artifacts/{namespace}/{workf
- Failed to get file from form:
- failed to stream artifact: %v
- CodeNotFound
- CodeNotImplemented
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/3f5786cb018f7c4d.
Report an issue: GitHub.