{"record":{"id":"929586c3bb15b9d6","repo":"hyperledger/fabric","slug":"cannot-unmarshal-file-part-s-into-a-block","errorCode":null,"errorMessage":"cannot unmarshal file part %s into a block","messagePattern":"cannot unmarshal file part (.+?) into a block","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"orderer/common/channelparticipation/restapi.go","lineNumber":463,"sourceCode":"\n\tfileHeader := form.File[FormDataConfigBlockKey][0]\n\tfile, err := fileHeader.Open()\n\tif err != nil {\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, \"cannot open file part %s from request body\", FormDataConfigBlockKey))\n\t\treturn nil\n\t}\n\n\tblockBytes, err := io.ReadAll(file)\n\tif err != nil {\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, \"cannot read file part %s from request body\", FormDataConfigBlockKey))\n\t\treturn nil\n\t}\n\n\tblock := &cb.Block{}\n\terr = proto.Unmarshal(blockBytes, block)\n\tif err != nil {\n\t\th.logger.Debugf(\"Failed to unmarshal blockBytes: %s\", err)\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrapf(err, \"cannot unmarshal file part %s into a block\", FormDataConfigBlockKey))\n\t\treturn nil\n\t}\n\n\treturn block\n}\n\n// Expect a multipart/form-data with a single part, of type file, with key FormDataConfigUpdateEnvelopeKey.\nfunc (h *HTTPHandler) multipartFormDataBodyToEnvelope(params map[string]string, req *http.Request, resp http.ResponseWriter) *cb.Envelope {\n\tboundary := params[\"boundary\"]\n\treader := multipart.NewReader(\n\t\thttp.MaxBytesReader(resp, req.Body, int64(h.config.MaxRequestBodySize)),\n\t\tboundary,\n\t)\n\tform, err := reader.ReadForm(2 * int64(h.config.MaxRequestBodySize))\n\tif err != nil {\n\t\th.sendResponseJsonError(resp, http.StatusBadRequest, errors.Wrap(err, \"cannot read form from request body\"))\n\t\treturn nil\n\t}","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/channelparticipation/restapi.go#L445-L481","documentation":"This error wraps a proto.Unmarshal failure: the bytes uploaded as the 'config-block' file part are not a valid protobuf-encoded common.Block. The handler logs the unmarshal failure at debug level and returns HTTP 400 with this message. It means the payload arrived intact but is not the expected block format (wrong file, corrupted, base64/JSON instead of raw protobuf bytes).","triggerScenarios":"POSTing to /participation/v1/channels/{channel}/join with a 'config-block' part whose bytes do not deserialize into a cb.Block — e.g. a JSON/block-string config, a transaction envelope, a PEM cert, or a text error page saved as the block file.","commonSituations":"Using 'configtxgen -outputBlock' output incorrectly transformed (base64-encoded by a script); downloading the genesis block via an API that returns JSON and re-uploading it raw; accidentally attaching the wrong file (TLS cert, channel tx); truncated block written by a failed fetch.","solutions":["Regenerate the genesis block with configtxgen (configtxgen -profile ... -outputBlock genesis.block) and upload that raw file","Ensure the client sends the raw protobuf block bytes, not base64 or JSON representations — decode first if your tooling encodes it","Verify the file with 'configtxgen -inspectBlock genesis.block' or 'osnadmin channel join' prerequisites before uploading","Confirm you are attaching the correct file (genesis/config block), not a config-update envelope or certificate"],"exampleFix":"# before: uploading base64-encoded block\nbase64 genesis.block | curl -X POST .../join -F 'config-block=@-;type=application/octet-stream'\n# after: upload raw block bytes\ncurl -X POST .../join -F 'config-block=@genesis.block'","handlingStrategy":"validation","validationCode":"// validate the block parses before uploading\nblock := &common.Block{}\nif err := proto.Unmarshal(blockBytes, block); err != nil {\n    return fmt.Errorf(\"%s is not a valid protobuf block: %w\", blockPath, err)\n}\n// optionally sanity-check the header\nif block.Header == nil || block.Header.Number != 0 {\n    return errors.New(\"expected a genesis block (number 0) with a header\")\n}","typeGuard":"func isProtoBlock(b []byte) bool {\n    blk := &common.Block{}\n    return proto.Unmarshal(b, blk) == nil && blk.Header != nil\n}","tryCatchPattern":"if resp.StatusCode == http.StatusBadRequest {\n    body, _ := io.ReadAll(resp.Body)\n    return fmt.Errorf(\"invalid config-block payload (check it is a raw protobuf block, not base64/JSON): %s\", body)\n}","preventionTips":["Generate blocks only via 'configtxgen -outputBlock' and upload the raw file","Never base64- or JSON-encode the block before upload","Validate locally with 'configtxgen -inspectBlock' before joining"],"tags":["fabric","protobuf","block","multipart-form"],"backgroundTag":"proto-unmarshal-failed","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}