{"record":{"id":"19ac3e1155d732fd","repo":"hyperledger/fabric","slug":"failed-marshaling-joinblock-proto-marshal-called","errorCode":null,"errorMessage":"failed marshaling joinblock: proto: Marshal called with nil","messagePattern":"failed marshaling joinblock: proto: Marshal called with nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/multichannel/registrar.go","lineNumber":705,"sourceCode":"\n\tif _, ok := r.followers[channelID]; ok {\n\t\treturn types.ChannelInfo{}, types.ErrChannelAlreadyExists\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif err2 := r.ledgerFactory.Remove(channelID); err2 != nil {\n\t\t\t\tlogger.Warningf(\"Failed to cleanup ledger: %v\", err2)\n\t\t\t}\n\t\t}\n\t}()\n\tledgerRes, clusterConsenter, err := r.initLedgerResourcesClusterConsenter(configBlock)\n\tif err != nil {\n\t\treturn types.ChannelInfo{}, err\n\t}\n\n\tif configBlock == nil {\n\t\treturn types.ChannelInfo{}, errors.Wrap(err, \"failed marshaling joinblock: proto: Marshal called with nil\")\n\t}\n\tblockBytes, err := proto.Marshal(configBlock)\n\tif err != nil {\n\t\treturn types.ChannelInfo{}, errors.Wrap(err, \"failed marshaling joinblock\")\n\t}\n\n\tif err := r.joinBlockFileRepo.Save(channelID, blockBytes); err != nil {\n\t\treturn types.ChannelInfo{}, errors.WithMessagef(err, \"failed saving joinblock to file repo for channel %s\", channelID)\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif err2 := r.removeJoinBlock(channelID); err2 != nil {\n\t\t\t\tlogger.Warningf(\"Failed to cleanup joinblock for channel %s: %v\", channelID, err2)\n\t\t\t}\n\t\t}\n\t}()\n\n\tisMember, err := clusterConsenter.IsChannelMember(configBlock)","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/multichannel/registrar.go#L687-L723","documentation":"JoinChannel marshals the supplied config block to bytes before persisting it to the join-block file repository. The code guards against a nil configBlock, and when it is nil it returns this wrapped error (the proto.Marshal nil panic message is baked into the message). A nil block means the caller passed no config block at all, which is unusable for joining the channel.","triggerScenarios":"Calling the JoinChannel administration API (channel participation join) with a nil config block — e.g. osnadmin channel join with a missing/failed-read join block, or an internal caller whose block loading failed silently.","commonSituations":"osnadmin pointed at a wrong file path so the block wasn't loaded; a client that passed the JSON body without the block bytes; reading a zero-byte genesis block file.","solutions":["Pass a valid genesis/config block: verify the file exists and is non-empty before calling join (osnadmin channel join --channelID X --config-block path)","Regenerate the genesis block with configtxgen if the file is empty or corrupt","Fix the caller to check the block-loading error before invoking JoinChannel instead of passing a nil block"],"exampleFix":"// before: block may be nil\nblock, _ := loadConfigBlock(path)\ninfo, err := registrar.JoinChannel(\"mychannel\", block)\n\n// after: fail fast on nil/empty block\nblock, err := loadConfigBlock(path)\nif err != nil || block == nil {\n\treturn fmt.Errorf(\"config block not loaded from %s: %w\", path, err)\n}\ninfo, err := registrar.JoinChannel(\"mychannel\", block)","handlingStrategy":"type-guard","validationCode":"// Go: nil/empty block check before calling JoinChannel\nif block == nil {\n    return errors.New(\"config block is nil; load a valid genesis block\")\n}\nif block.Data == nil || len(block.Data.Data) == 0 {\n    return errors.New(\"config block has empty data\")\n}","typeGuard":"func isUsableConfigBlock(b *common.Block) bool {\n    return b != nil && b.Header != nil && b.Data != nil && len(b.Data.Data) > 0\n}","tryCatchPattern":"info, err := registrar.JoinChannel(channelID, configBlock)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed marshaling joinblock\") {\n        return fmt.Errorf(\"nil/invalid config block supplied for channel %s: %w\", channelID, err)\n    }\n    return err\n}","preventionTips":["Check file size > 0 and parse with protoutil.UnmarshalBlock before submitting to osnadmin join","Always check the error from block-loading functions; never pass a possibly-nil block onward","Use configtxgen output directly as the join block source"],"tags":["hyperledger-fabric","orderer","join-channel","nil-block"],"backgroundTag":"nil-config-block","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"}