{"record":{"id":"aa00012c6d17d7da","repo":"hyperledger/fabric","slug":"error-unmarshalling-lastconfig","errorCode":null,"errorMessage":"error unmarshalling LastConfig","messagePattern":"error unmarshalling LastConfig","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"protoutil/blockutils.go","lineNumber":197,"sourceCode":"}\n\n// GetLastConfigIndexFromBlock retrieves the index of the last config block as\n// encoded in the block metadata\nfunc GetLastConfigIndexFromBlock(block *cb.Block) (uint64, error) {\n\tm, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)\n\tif err != nil {\n\t\treturn 0, errors.WithMessage(err, \"failed to retrieve metadata\")\n\t}\n\t// TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer\n\tif len(m.Value) == 0 {\n\t\tm, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)\n\t\tif err != nil {\n\t\t\treturn 0, errors.WithMessage(err, \"failed to retrieve metadata\")\n\t\t}\n\t\tlc := &cb.LastConfig{}\n\t\terr = proto.Unmarshal(m.Value, lc)\n\t\tif err != nil {\n\t\t\treturn 0, errors.Wrap(err, \"error unmarshalling LastConfig\")\n\t\t}\n\t\treturn lc.Index, nil\n\t}\n\n\tobm := &cb.OrdererBlockMetadata{}\n\terr = proto.Unmarshal(m.Value, obm)\n\tif err != nil {\n\t\treturn 0, errors.Wrap(err, \"failed to unmarshal orderer block metadata\")\n\t}\n\treturn obm.LastConfig.Index, nil\n}\n\n// GetLastConfigIndexFromBlockOrPanic retrieves the index of the last config\n// block as encoded in the block metadata, or panics on error\nfunc GetLastConfigIndexFromBlockOrPanic(block *cb.Block) uint64 {\n\tindex, err := GetLastConfigIndexFromBlock(block)\n\tif err != nil {\n\t\tpanic(err)","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/protoutil/blockutils.go#L179-L215","documentation":"GetLastConfigIndexFromBlock reads the ORDERER block metadata entry. In the legacy path it unmarshals the entry first as cb.LastConfig; if those bytes are not a valid LastConfig protobuf, this wrapped error is returned. It means the signatures-metadata entry contents are corrupt or not in the expected legacy format.","triggerScenarios":"Calling GetLastConfigIndexFromBlock on a block whose metadata[BlockMetadataIndex_LAST_CONFIG] bytes fail proto.Unmarshal into cb.LastConfig — corrupt ledger data, or a metadata entry produced with a different layout.","commonSituations":"Reading blocks from a damaged file ledger, mixing Fabric versions where the LAST_CONFIG index semantics differ, or custom block producers writing non-standard metadata.","solutions":["Re-fetch the block from a healthy ordering node or peer ledger","Validate metadata length and that the entry is non-empty before parsing","Use GetLastConfigIndexFromBlockOrPanic only on blocks known to be valid; prefer the error-returning API in recovery code","Rebuild the ledger (e.g. rejoin the channel) if block data is permanently corrupt"],"exampleFix":"// before\nlc := &cb.LastConfig{}\nerr = proto.Unmarshal(m.Value, lc)\nif err != nil {\n    return 0, errors.Wrap(err, \"error unmarshalling LastConfig\")\n}\n// after\nif len(m.Value) == 0 {\n    return 0, errors.New(\"LAST_CONFIG metadata entry is empty\")\n}\nlc := &cb.LastConfig{}\nif err := proto.Unmarshal(m.Value, lc); err != nil {\n    return 0, errors.Wrapf(err, \"error unmarshalling LastConfig (%d bytes)\", len(m.Value))\n}","handlingStrategy":"validation","validationCode":"func hasLastConfigEntry(block *cb.Block) bool {\n    md := block.GetMetadata().GetMetadata()\n    return len(md) >= int(cb.BlockMetadataIndex_LAST_CONFIG)+1 && len(md[cb.BlockMetadataIndex_LAST_CONFIG]) > 0\n}","typeGuard":"func isParseableLastConfig(raw []byte) (*cb.LastConfig, bool) {\n    lc := &cb.LastConfig{}\n    if len(raw) == 0 || proto.Unmarshal(raw, lc) != nil {\n        return nil, false\n    }\n    return lc, true\n}","tryCatchPattern":"idx, err := GetLastConfigIndexFromBlock(block)\nif err != nil {\n    return fmt.Errorf(\"cannot locate last config in block %d: %w\", block.GetHeader().GetNumber(), err)\n}","preventionTips":["Use the error-returning API rather than the OrPanic variant in production code","Guard against empty metadata entries before parsing","Restore ledgers only from verified backups","Keep Fabric versions consistent across the network"],"tags":["protobuf","unmarshal","last-config","block-metadata"],"backgroundTag":"protobuf-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"}