{"record":{"id":"525559189b47a019","repo":"hyperledger/fabric","slug":"failed-to-read-raft-metadata","errorCode":null,"errorMessage":"failed to read Raft metadata","messagePattern":"failed to read Raft metadata","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"orderer/consensus/etcdraft/consenter.go","lineNumber":142,"sourceCode":"\n\tif m.GetOptions() == nil {\n\t\treturn nil, errors.New(\"etcdraft options have not been provided\")\n\t}\n\n\tisMigration := (metadata == nil || len(metadata.GetValue()) == 0) && (support.Height() > 1)\n\tif isMigration {\n\t\tc.Logger.Debugf(\"Block metadata is nil at block height=%d, it is consensus-type migration\", support.Height())\n\t}\n\n\t// determine raft replica set mapping for each node to its id\n\t// for newly started chain we need to read and initialize raft\n\t// metadata by creating mapping between conseter and its id.\n\t// In case chain has been restarted we restore raft metadata\n\t// information from the recently committed block meta data\n\t// field.\n\tblockMetadata, err := ReadBlockMetadata(metadata, m)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to read Raft metadata\")\n\t}\n\n\tconsenters := CreateConsentersMap(blockMetadata, m)\n\n\tid, err := c.detectSelfID(consenters)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"without a system channel, a follower should have been created\")\n\t}\n\n\tvar evictionSuspicion time.Duration\n\tif c.EtcdRaftConfig.EvictionSuspicion == \"\" {\n\t\tc.Logger.Infof(\"EvictionSuspicion not set, defaulting to %v\", DefaultEvictionSuspicion)\n\t\tevictionSuspicion = DefaultEvictionSuspicion\n\t} else {\n\t\tevictionSuspicion, err = time.ParseDuration(c.EtcdRaftConfig.EvictionSuspicion)\n\t\tif err != nil {\n\t\t\tc.Logger.Panicf(\"Failed parsing Consensus.EvictionSuspicion: %s: %v\", c.EtcdRaftConfig.EvictionSuspicion, err)\n\t\t}","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/etcdraft/consenter.go#L124-L160","documentation":"HandleChain (orderer/consensus/etcdraft/consenter.go:142) calls ReadBlockMetadata to reconcile the raft metadata stored in the last block's metadata field with the consenters declared in the channel config. This error wraps a mismatch: counts differ, a config consenter is missing from block metadata, or an ID is out of range. It prevents starting with an inconsistent consenter-to-ID mapping after restart or config change.","triggerScenarios":"Orderer restart/rejoin where the last committed block's raft metadata disagrees with the channel config's ConfigMetadata consenters (different number of consenters, unknown consenter, or ID not in [1, len]).","commonSituations":"Changing the consenter set in config while the local node is behind (has not committed the config block); restoring an orderer's ledger from a backup older than a config update; corrupted or truncated block metadata after crash.","solutions":["Pull and commit the latest blocks from the ordering service so the node sees the config update block before starting raft","Restore the orderer's ledger/filesystem from a consistent backup taken after the last config change","Compare the number of block-metadata consenters with ConfigMetadata consenters; both must match one-to-one","For irreparable local state, rejoin the node with a fresh genesis and sync (or re-add it via config update)"],"exampleFix":"// before: starting node with stale ledger missing new consenter\n# ledger height < config block adding consenter -> counts mismatch\n// after\n# fetch latest blocks: peer/orderer resync from service endpoint, then restart orderer","handlingStrategy":"validation","validationCode":"blockMetadata, err := ReadBlockMetadata(metadata, configMetadata)\nif err != nil {\n    // resync ledger from the ordering service or restore consistent backup before starting raft\n    return fmt.Errorf(\"block/config metadata mismatch, resync needed: %w\", err)\n}","typeGuard":"func metadataConsistent(blockMeta []*common.Metadata, cfg *etcdraft.ConfigMetadata) bool {\n    return len(blockMeta) == len(cfg.GetConsenters())\n}","tryCatchPattern":"chain, err := consenter.HandleChain(support, metadata)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to read Raft metadata\") {\n        // fetch latest blocks / restore consistent backup, then retry startup\n    }\n}","preventionTips":["Take ledger backups only after config-update blocks are committed","Let nodes sync to the latest block before restarting after consenter changes","Verify consenter counts match between block metadata and channel config"],"tags":["raft","metadata","blockchain","restart"],"backgroundTag":"raft-metadata-mismatch","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}