valyala/fasthttp · error

error when closing multipart form writer: %w

Error message

error when closing multipart form writer: %w

What it means

fasthttp wraps the error returned by multipart.Writer.Close when finalizing a multipart/form-data body (writing the terminating boundary). Failure means the encoded body is incomplete and the request cannot be sent correctly.

Source

Thrown at http.go:1264

			if err != nil {
				return fmt.Errorf("cannot create form file %q (%q): %w", k, fv.Filename, err)
			}
			fh, err := fv.Open()
			if err != nil {
				return fmt.Errorf("cannot open form file %q (%q): %w", k, fv.Filename, err)
			}
			if _, err = copyZeroAlloc(vw, fh); err != nil {
				_ = fh.Close()
				return fmt.Errorf("error when copying form file %q (%q): %w", k, fv.Filename, err)
			}
			if err = fh.Close(); err != nil {
				return fmt.Errorf("cannot close form file %q (%q): %w", k, fv.Filename, err)
			}
		}
	}

	if err := mw.Close(); err != nil {
		return fmt.Errorf("error when closing multipart form writer: %w", err)
	}

	return nil
}

func readMultipartForm(r io.Reader, boundary string, size, maxInMemoryFileSize int) (*multipart.Form, error) {
	// Do not care about memory allocations here, since they are tiny
	// compared to multipart data (aka multi-MB files) usually sent
	// in multipart/form-data requests.

	if size <= 0 {
		return nil, fmt.Errorf("form size must be greater than 0: given %d", size)
	}
	lr := io.LimitReader(r, int64(size))
	mr := multipart.NewReader(lr, boundary)
	f, err := mr.ReadForm(int64(maxInMemoryFileSize))
	if err != nil {
		return nil, fmt.Errorf("cannot read multipart/form-data body: %w", err)

View on GitHub (pinned to c96f600972)

Solutions

  1. Reset or recreate the Request before retrying (req.Reset()).
  2. Check the wrapped cause for the writer's underlying failure.
  3. Validate the multipart form boundary contains only legal boundary characters.
  4. Upgrade fasthttp if the trigger involves writer reuse after an error.

Example fix

// before
if err := client.Do(req, resp); err != nil {
    client.Do(req, resp) // retry on same corrupted req
}
// after
if err := client.Do(req, resp); err != nil {
    req.Reset()
    req.SetFormFile("f", path)
    client.Do(req, resp)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if req.MultipartForm != nil && len(req.MultipartFormBoundary) == 0 {
    return errors.New("multipart form without boundary set")
}

Type guard

func hasValidBoundary(b string) bool {
    if len(b) == 0 || len(b) > 70 { return false }
    for _, r := range b {
        if !isBoundaryChar(r) { return false }
    }
    return true
}

Try / catch

if err := client.Do(req, resp); err != nil {
    if strings.Contains(err.Error(), "closing multipart form writer") {
        req.Reset()
        return rebuildAndRetry()
    }
    return err
}

Prevention

When it happens

Trigger: Finishing marshaling of req.MultipartForm in writeMultipartForm (via client.Do / request writing) when mw.Close() fails — typically after an earlier part write left the underlying buffer/writer in a bad state.

Common situations: Memory/buffer allocation failure in the writer's target; reusing a Request whose multipart writer state is corrupted from a previous aborted write; custom multipartFormBoundary containing invalid characters.

Related errors


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/a5d719150dcb6f85. Report an issue: GitHub.