valyala/fasthttp · error

cannot close form file %q (%q): %w

Error message

cannot close form file %q (%q): %w

What it means

fasthttp wraps the error returned by closing the multipart part file handle after copying its contents into a multipart/form-data body. Close can fail especially for files written via os.File (flush/sync failures) or custom closers.

Source

Thrown at http.go:1258

	}

	// marshal files
	for k, fvv := range f.File {
		for _, fv := range fvv {
			vw, err := mw.CreatePart(fv.Header)
			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)

View on GitHub (pinned to c96f600972)

Solutions

  1. Inspect the wrapped cause; most Close errors indicate a resource issue with the underlying reader.
  2. Do not double-close the handle you passed to fasthttp before the request completes.
  3. Fix the custom ReadCloser's Close implementation to be idempotent and error-free.
  4. Use SetFormFileContent with a plain byte slice if streaming close semantics are not needed.

Example fix

// before
type rc struct{ io.Reader }
func (r rc) Close() error { return errors.New("flush failed") }
req.SetFormFileContent("f", rc{bytes.NewReader(data)}, "a.txt")
// after
req.SetFormFileContent("f", data, "a.txt") // bytes avoid Close paths
Defensive patterns

Strategy: validation

Type guard

func safeCloser(c io.ReadCloser) io.ReadCloser {
    return idempotentCloser{c}
}
type idempotentCloser struct{ io.ReadCloser }
func (i idempotentCloser) Close() error {
    err := i.ReadCloser.Close()
    i.ReadCloser = nopCloser{}
    return err
}

Try / catch

if err := client.Do(req, resp); err != nil {
    if strings.Contains(err.Error(), "cannot close form file") {
        log.Printf("form file close failed (upload likely still sent): %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: After a successful copyZeroAlloc, fv.Open()'s handle's Close() returns a non-nil error while writing a request with form file fields (SetFormFile / SetFormFileContent with a Closer that errors).

Common situations: Custom io.ReadCloser implementations whose Close reports a deferred write error; tmpfile-based uploads where the underlying descriptor was already closed by user code; network-backed readers whose Close does a final flush that fails.

Related errors


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