hyperledger/fabric · error

cannot unmarshal file part

Error message

cannot unmarshal file part %s into an envelope

What it means

This HTTP 400 error wraps a proto.Unmarshal failure when the bytes from the multipart file part cannot be decoded into a common.Envelope. The endpoint expects the file part to contain a protobuf-marshaled cb.Envelope (a config update transaction); any other content - JSON, PEM, base64 text, a genesis block, or arbitrary data - fails unmarshaling.

Solutions

  1. Verify the file contains raw protobuf Envelope bytes (e.g. produced by configtxlator or configtxgen tooling), not JSON or base64 - decode base64 first if applicable.
  2. Re-generate the config update envelope: create the config update with configtxlator, compute the update, sign it, and marshal it as an Envelope.
  3. Ensure you are not uploading a genesis block or common.Block; this API takes only the config update Envelope.
  4. Check the file is complete (compare byte size/checksum against the producer) and that no wrapper text or HTML was captured into it.

Example fix

// before: uploading base64-encoded or JSON data
body := base64.StdEncoding.EncodeToString(envelopeBytes)
// after: upload the raw protobuf envelope bytes
body := envelopeBytes // proto.Marshal(&cb.Envelope{...}) output, written directly
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeEnvelope(bytes: Uint8Array): boolean {
  // wire-format check: Envelope.ChannelHeader is field 1, bytes => tag 0x0a followed by varint length
  if (bytes.length < 2) return false
  return bytes[0] === 0x0a && bytes[1] > 0
}
// call before upload:
if (!looksLikeEnvelope(envelopeBytes)) throw new Error('file is not raw protobuf Envelope bytes (JSON/base64/block?)')

Type guard

function isProtoEnvelopeBytes(x: unknown): x is Uint8Array {
  return x instanceof Uint8Array && x.length > 1 && x[0] === 0x0a
}

Try / catch

try {
  const resp = await fetch(url, { method: 'PUT', body: formData })
  if (resp.status === 400) {
    const msg = await resp.text()
    if (msg.includes('cannot unmarshal file part')) {
      // bytes were not a protobuf Envelope: check for base64/JSON and re-encode
    }
  }
} catch (e) { /* transport error */ }

Prevention

When it happens

Trigger: The uploaded file part contains bytes that are not a valid protobuf Envelope: a JSON config update, a base64-encoded envelope, a common.Block/genesis block, an HTML error page, or a truncated envelope.

Common situations: Uploading a genesis block or channel config JSON instead of the signed config update envelope; exporting the envelope but base64-encoding it (osnadmin/channel join scripts encode to base64 while this API expects raw proto bytes); pointing the client at an HTML login/error page captured as the body; sending a partially written file.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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

Appendix: source

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

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

	envelopeBytes, err := io.ReadAll(file)
	if err != nil {
		h.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, "cannot read file part %s from request body", FormDataConfigUpdateEnvelopeKey))
		return nil
	}

	envelope := &cb.Envelope{}
	err = proto.Unmarshal(envelopeBytes, envelope)
	if err != nil {
		h.logger.Debugf("Failed to unmarshal envelopeBytes: %s", err)
		h.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, "cannot unmarshal file part %s into an envelope", FormDataConfigUpdateEnvelopeKey))
		return nil
	}

	return envelope
}

func (h *HTTPHandler) extractChannelID(req *http.Request, resp http.ResponseWriter) (string, error) {
	channelID, ok := mux.Vars(req)[channelIDKey]
	if !ok {
		err := errors.New("missing channel ID")
		h.sendResponseJsonError(resp, http.StatusInternalServerError, err)
		return "", err
	}

	if err := configtx.ValidateChannelID(channelID); err != nil {
		err = errors.WithMessage(err, "invalid channel ID")
		h.sendResponseJsonError(resp, http.StatusBadRequest, err)
		return "", err

View on GitHub (pinned to 2736b63f8f)