{"record":{"id":"77030c4011150611","repo":"hyperledger/fabric","slug":"envelope-unmarshalling-failed","errorCode":null,"errorMessage":"envelope unmarshalling failed","messagePattern":"envelope unmarshalling failed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/msgprocessor/maintenancefilter.go","lineNumber":64,"sourceCode":"\t\tsupport:                       support,\n\t\tpermittedTargetConsensusTypes: make(map[string]bool),\n\t\tbccsp:                         bccsp,\n\t}\n\tmf.permittedTargetConsensusTypes[\"BFT\"] = true\n\treturn mf\n}\n\n// Apply applies the maintenance filter on a CONFIG tx.\nfunc (mf *MaintenanceFilter) Apply(message *cb.Envelope) error {\n\tordererConf, ok := mf.support.OrdererConfig()\n\tif !ok {\n\t\tlogger.Panic(\"Programming error: orderer config not found\")\n\t}\n\n\tconfigEnvelope := &cb.ConfigEnvelope{}\n\tchanHdr, err := protoutil.UnmarshalEnvelopeOfType(message, cb.HeaderType_CONFIG, configEnvelope)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"envelope unmarshalling failed\")\n\t}\n\n\tlogger.Debugw(\"Going to inspect maintenance mode transition rules\",\n\t\t\"ConsensusState\", ordererConf.ConsensusState(), \"channel\", chanHdr.ChannelId)\n\terr = mf.inspect(configEnvelope, ordererConf)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"config transaction inspection failed\")\n\t}\n\n\treturn nil\n}\n\n// inspect checks whether the next orderer config, extracted from the incoming configEnvelope, respects the\n// transition rules of consensus-type migration using maintenance-mode.\nfunc (mf *MaintenanceFilter) inspect(configEnvelope *cb.ConfigEnvelope, ordererConfig channelconfig.Orderer) error {\n\tif configEnvelope.LastUpdate == nil {\n\t\treturn errors.Errorf(\"updated config does not include a config update\")\n\t}","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/msgprocessor/maintenancefilter.go#L46-L82","documentation":"MaintenanceFilter.Apply wraps any failure to decode the incoming message as a CONFIG-type envelope (via protoutil.UnmarshalEnvelopeOfType) with this message. It means the envelope's payload could not be unmarshalled into a cb.ConfigEnvelope, or the envelope's header type/channel did not match HeaderType_CONFIG. The config transaction is therefore rejected before any migration-rule inspection occurs.","triggerScenarios":"A transaction with HeaderType_CONFIG reaches Apply but its payload is not a valid serialized ConfigEnvelope (corrupted, truncated, wrong proto encoding, or the header claims CONFIG while the payload is a different message type).","commonSituations":"A client or tooling script submits a malformed config envelope to the ordering service; a proxy/middleware re-encodes the envelope incorrectly; a fabric version upgrade changes proto serialization (gogo vs protobuf-go apiv2) and old bytes no longer parse.","solutions":["Verify the client constructs the envelope with protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, signer, configEnv, 0, 0)","Check that the payload bytes are a marshalled cb.ConfigEnvelope, not a cb.ConfigUpdateEnvelope (which is only for the prepare/config-update step)","Re-serialize with the same protobuf library generation used by the orderer (protos-go-apiv2 / protobuf-go)","Inspect the wrapped cause via errors.Cause to see whether it is a header-type mismatch or a proto parse error"],"exampleFix":"// before: submitting a ConfigUpdateEnvelope directly as a CONFIG tx\nenv := protoutil.MarshalOrPanic(configUpdateEnv)\n// after: wrap the config update into a full ConfigEnvelope and create a proper CONFIG envelope\nconfigEnv := &cb.ConfigEnvelope{Config: nextConfig, LastUpdate: configUpdateEnv}\nsignedEnv, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, chID, signer, configEnv, 0, 0)","handlingStrategy":"validation","validationCode":"hdr := &cb.ChannelHeader{}\nif err := proto.Unmarshal(env.Payload, &payload); err != nil { return err }\nif err := proto.Unmarshal(payload.Header.ChannelHeader, hdr); err != nil { return err }\nif hdr.Type != int32(cb.HeaderType_CONFIG) { return fmt.Errorf(\"not a CONFIG tx\") }\nce := &cb.ConfigEnvelope{}\nif err := proto.Unmarshal(payload.Data, ce); err != nil { return err }\nif ce.LastUpdate == nil { return errors.New(\"LastUpdate missing\") }","typeGuard":"func isConfigEnvelope(env *cb.Envelope) bool {\n\tp := &cb.Payload{}\n\tif proto.Unmarshal(env.GetPayload(), p) != nil || p.GetHeader() == nil { return false }\n\treturn p.Header.ChannelHeader != nil && proto.Unmarshal(p.Header.ChannelHeader, &cb.ChannelHeader{}) == nil\n}","tryCatchPattern":"if err := filter.Apply(env); err != nil {\n\tif strings.Contains(err.Error(), \"envelope unmarshalling failed\") {\n\t\tlog.Errorf(\"malformed CONFIG envelope: %v\", errors.Cause(err))\n\t\treturn ErrBadEnvelope\n\t}\n\treturn err\n}","preventionTips":["Always build CONFIG envelopes with protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, ...)","Never submit a ConfigUpdateEnvelope where a ConfigEnvelope is required","Keep fabric-protos-go-apiv2 versions in sync between client and orderer","Decode-and-recheck with configtxlator before submitting generated envelopes"],"tags":["ordering","protobuf","config-transaction","envelope"],"backgroundTag":"envelope-unmarshalling-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"}