{"record":{"id":"ecf8b9c8828c5db5","repo":"hyperledger/fabric","slug":"inner-channelheader-is-nil","errorCode":null,"errorMessage":"inner channelheader is nil","messagePattern":"inner channelheader is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/smartbft/configverifier.go","lineNumber":206,"sourceCode":"\n\treturn nil\n}\n\nfunc (cbv *ConfigBlockValidator) verifyConfigUpdateMsg(outEnv *common.Envelope, confEnv *common.ConfigEnvelope, chdr *common.ChannelHeader) error {\n\tif confEnv == nil || confEnv.LastUpdate == nil || confEnv.Config == nil {\n\t\treturn errors.New(\"invalid config envelope\")\n\t}\n\tenvPayload, err := protoutil.UnmarshalPayload(confEnv.LastUpdate.Payload)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif envPayload.Header == nil {\n\t\treturn errors.New(\"inner header is nil\")\n\t}\n\n\tif envPayload.Header.ChannelHeader == nil {\n\t\treturn errors.New(\"inner channelheader is nil\")\n\t}\n\n\ttyp := common.HeaderType(chdr.Type)\n\n\tcbv.Logger.Infof(\"Applying filters for config update of type %s to channel %s\", typ, chdr.ChannelId)\n\n\t// First apply the filters on the outer envelope, regardless of the type of transaction it is.\n\tif err := cbv.Filters.ApplyFilters(chdr.ChannelId, outEnv); err != nil {\n\t\treturn err\n\t}\n\n\tvar expectedConfigEnv *common.ConfigEnvelope\n\tchannelID, err := protoutil.ChannelID(confEnv.LastUpdate)\n\tif err != nil {\n\t\treturn errors.Errorf(\"error extracting channel ID from config update\")\n\t}\n\n\tif cbv.ValidatingChannel != channelID {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/smartbft/configverifier.go#L188-L224","documentation":"verifyConfigUpdateMsg guard: the payload inside the config envelope's LastUpdate envelope has a nil ChannelHeader. The inner (last-update) message is structurally incomplete, so its type and channel cannot be checked.","triggerScenarios":"envPayload.Header != nil but envPayload.Header.ChannelHeader == nil in verifyConfigUpdateMsg during ValidateConfig.","commonSituations":"Hand-built envelopes where only SignatureHeader was set; tooling that strips the ChannelHeader; deserialization issues that leave ChannelHeader bytes empty.","solutions":["Set envPayload.Header.ChannelHeader to a marshaled common.ChannelHeader with correct Type=CONFIG_UPDATE and ChannelId","Regenerate the envelope with an SDK's config-update builder","Validate the envelope client-side (protoutil-style checks) before submission"],"exampleFix":"// before\nheader := &common.Header{SignatureHeader: shdr}\n// after\nheader := &common.Header{ChannelHeader: chdrBytes, SignatureHeader: shdr}","handlingStrategy":"type-guard","validationCode":"if envPayload.Header.ChannelHeader == nil {\n    return errors.New(\"inner channel header is missing\")\n}","typeGuard":"func hasChannelHeader(h *common.Header) bool {\n    return h != nil && h.ChannelHeader != nil\n}","tryCatchPattern":null,"preventionTips":["Set both ChannelHeader and SignatureHeader in every payload header","Re-check envelopes after any transformation (signing, marshaling)","Add unit tests asserting ChannelHeader presence in generated config updates"],"tags":["hyperledger-fabric","smartbft","channel-header","malformed-input"],"backgroundTag":"malformed-config-envelope","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"}