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

  1. Send a proper multipart/form-data body: curl -F 'file=@path' (not -d/--data-binary)
  2. Ensure the Content-Type header includes the multipart boundary (let the HTTP client set it automatically)
  3. Retry the upload if the connection was interrupted/truncated
  4. 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

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

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/3f5786cb018f7c4d. Report an issue: GitHub.