hyperledger/fabric · warning

cannot read form from request body

Error message

cannot read form from request body

What it means

After determining the multipart boundary, multipartFormDataBodyToBlock streams the request body through multipart.Reader.ReadForm with a size cap of MaxRequestBodySize (body) and twice that for form memory. If the form cannot be parsed — malformed multipart syntax, truncated body, or body exceeding MaxBytesReader's limit — the handler returns 400 with this wrapped error.

Source

Thrown at orderer/common/channelparticipation/restapi.go:432

		h.sendUpdateError(err, resp)
		return
	}
	info.URL = path.Join(URLBaseV1Channels, info.Name)

	h.logger.Debugf("Successfully update config channel: %s", info.URL)
	h.sendResponseCreated(resp, info.URL, info)
}

// Expect a multipart/form-data with a single part, of type file, with key FormDataConfigBlockKey.
func (h *HTTPHandler) multipartFormDataBodyToBlock(params map[string]string, req *http.Request, resp http.ResponseWriter) *cb.Block {
	boundary := params["boundary"]
	reader := multipart.NewReader(
		http.MaxBytesReader(resp, req.Body, int64(h.config.MaxRequestBodySize)),
		boundary,
	)
	form, err := reader.ReadForm(2 * int64(h.config.MaxRequestBodySize))
	if err != nil {
		h.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrap(err, "cannot read form from request body"))
		return nil
	}

	if _, exist := form.File[FormDataConfigBlockKey]; !exist {
		h.sendResponseJsonError(resp, http.StatusBadRequest, errors.Errorf("form does not contains part key: %s", FormDataConfigBlockKey))
		return nil
	}

	if len(form.File) != 1 || len(form.Value) != 0 {
		h.sendResponseJsonError(resp, http.StatusBadRequest, errors.New("form contains too many parts"))
		return nil
	}

	fileHeader := form.File[FormDataConfigBlockKey][0]
	file, err := fileHeader.Open()
	if err != nil {
		h.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, "cannot open file part %s from request body", FormDataConfigBlockKey))
		return nil

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Increase channelParticipation.maxRequestBodySize in orderer.yaml if the config block is large, and restart
  2. Re-send with a correct multipart form (curl -F 'config-block=@genesis.block') so boundaries and parts are well-formed
  3. Verify the uploaded config block file is complete and not truncated (compare checksums)
  4. Check the wrapped inner error: http: request body too large means raise the size limit; multipart errors mean fix the body format

Example fix

// before (orderer.yaml)
ChannelParticipation:
  MaxRequestBodySize: 1048576
// after
ChannelParticipation:
  MaxRequestBodySize: 10485760
Defensive patterns

Strategy: try-catch

Validate before calling

fi, _ := os.Stat(configBlockPath)
if fi.Size() > maxRequestBodySize {
	return fmt.Errorf("config block is %d bytes; raise channelParticipation.maxRequestBodySize (current %d)", fi.Size(), maxRequestBodySize)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "cannot read form from request body") {
	if strings.Contains(err.Error(), "request body too large") {
		// raise ChannelParticipation.MaxRequestBodySize in orderer.yaml
	} else {
		// malformed multipart: rebuild with curl -F / multipart writer
	}
}

Prevention

When it happens

Trigger: serveJoin receiving a body whose multipart encoding is invalid (wrong boundary, missing final boundary, truncated upload) or whose size exceeds orderer config channelParticipation.maxRequestBodySize, causing MaxBytesReader to cut the stream.

Common situations: Config block larger than MaxRequestBodySize (default 1MB) when joining a channel with a big config; network interruption during upload; client library generating a different boundary than advertised in the header; request body modified by middleware.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/21084d0da7f00add. Report an issue: GitHub.