{"record":{"id":"ac5a49d143242fb7","repo":"hyperledger/fabric","slug":"unexpected-previous-block-hash-expected-previoush","errorCode":null,"errorMessage":"unexpected Previous block hash. Expected PreviousHash = [%x], PreviousHash referred in the latest block= [%x]","messagePattern":"unexpected Previous block hash\\. Expected PreviousHash = \\[%x\\], PreviousHash referred in the latest block= \\[%x\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"common/ledger/blkstorage/blockfile_mgr.go","lineNumber":298,"sourceCode":"\tmgr.currentFileWriter = nextFileWriter\n\tmgr.updateBlockfilesInfo(blkfilesInfo)\n}\n\nfunc (mgr *blockfileMgr) addBlock(block *common.Block) error {\n\tbcInfo := mgr.getBlockchainInfo()\n\tif block.Header.Number != bcInfo.Height {\n\t\treturn errors.Errorf(\n\t\t\t\"block number should have been %d but was %d\",\n\t\t\tmgr.getBlockchainInfo().Height, block.Header.Number,\n\t\t)\n\t}\n\n\t// Add the previous hash check - Though, not essential but may not be a bad idea to\n\t// verify the field `block.Header.PreviousHash` present in the block.\n\t// This check is a simple bytes comparison and hence does not cause any observable performance penalty\n\t// and may help in detecting a rare scenario if there is any bug in the ordering service.\n\tif !bytes.Equal(block.Header.PreviousHash, bcInfo.CurrentBlockHash) {\n\t\treturn errors.Errorf(\n\t\t\t\"unexpected Previous block hash. Expected PreviousHash = [%x], PreviousHash referred in the latest block= [%x]\",\n\t\t\tbcInfo.CurrentBlockHash, block.Header.PreviousHash,\n\t\t)\n\t}\n\tblockBytes, info := serializeBlock(block)\n\tblockHash := protoutil.BlockHeaderHash(block.Header)\n\t// Get the location / offset where each transaction starts in the block and where the block ends\n\ttxOffsets := info.txOffsets\n\tcurrentOffset := mgr.blockfilesInfo.latestFileSize\n\n\tblockBytesLen := len(blockBytes)\n\tblockBytesEncodedLen := protowire.AppendVarint(nil, uint64(blockBytesLen))\n\ttotalBytesToAppend := blockBytesLen + len(blockBytesEncodedLen)\n\n\t// Determine if we need to start a new file since the size of this block\n\t// exceeds the amount of space left in the current file\n\tif currentOffset+totalBytesToAppend > mgr.conf.maxBlockfileSize {\n\t\tmgr.moveToNextFile()","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/ledger/blkstorage/blockfile_mgr.go#L280-L316","documentation":"addBlock verifies that block.Header.PreviousHash equals the hash of the current chain tip (bcInfo.CurrentBlockHash). This cheap bytes comparison catches blocks that do not chain onto the ledger's last block, which usually indicates a fork, a block from a different chain, or an ordering-service bug. The block is rejected with this error.","triggerScenarios":"Adding a block whose PreviousHash differs from the stored hash of the last committed block — e.g., after restoring block files inconsistently, importing blocks from a diverged branch, or two orderers producing conflicting chains.","commonSituations":"Ledger restored from mixed/partial backups; peer connected to a diverged ordering service (Raft reconfiguration/fork); snapshot import combined with blocks from a different network; test harnesses reusing blocks across ledgers.","solutions":["Verify the block source: reconnect to the correct ordering service / consensus cluster and re-fetch blocks from the ledger height","Restore the ledger data from a consistent backup matching the same chain (same genesis block and orderer history)","If the chain diverged, perform a ledger reset and re-sync from the canonical chain","Check that genesis blocks/config match across environments before importing or syncing"],"exampleFix":"// before: trusting a stale/mismatched block feed\nblk := fetchFromOrderer(number)\nstore.AddBlock(blk) // fails: PreviousHash != tip\n// after: re-sync from ledger height against the canonical orderer\nbcInfo, _ := store.GetBlockchainInfo()\nblk := fetchFromCanonicalOrderer(bcInfo.Height)\nstore.AddBlock(blk)","handlingStrategy":"validation","validationCode":"bcInfo, err := store.GetBlockchainInfo()\nif err != nil { return err }\nif !bytes.Equal(block.Header.PreviousHash, bcInfo.CurrentBlockHash) {\n    return fmt.Errorf(\"refusing block %d: does not chain onto tip\", block.Header.Number)\n}","typeGuard":null,"tryCatchPattern":"if err := store.AddBlock(block); err != nil {\n    if strings.Contains(err.Error(), \"unexpected Previous block hash\") {\n        // halt the feed, flag a possible fork/divergence, re-sync from canonical orderer\n    }\n    return err\n}","preventionTips":["Always source blocks from the single canonical ordering service/consensus cluster","Restore ledgers only from backups taken on the same chain (same genesis/history)","Validate PreviousHash against GetBlockchainInfo() before committing when driving the API directly","Investigate any occurrence immediately — it usually signals a fork or corrupted/divergent ledger"],"tags":["hyperledger-fabric","ledger","hash-mismatch","fork","chain-integrity"],"backgroundTag":"previous-hash-mismatch","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"}