{"record":{"id":"264409032800c714","repo":"hyperledger/fabric","slug":"block-header-previous-hash-mismatch-on-sequence-d","errorCode":null,"errorMessage":"block header previous hash mismatch on sequence %d, expected %x, got %x","messagePattern":"block header previous hash mismatch on sequence (.+?), expected %x, got %x","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"orderer/common/follower/follower_chain.go","lineNumber":541,"sourceCode":"\t\tactualPrevHash = protoutil.BlockHeaderHash(prevBlock.Header)\n\t}\n\n\t// Pull until the latest height\n\tfor seq := firstBlockToPull; seq < targetHeight; seq++ {\n\t\tn := seq - firstBlockToPull\n\t\tselect {\n\t\tcase <-c.stopChan:\n\t\t\tc.logger.Debug(\"Received a stop signal\")\n\t\t\treturn n, ErrChainStopped\n\t\tdefault:\n\t\t\tnextBlock := c.blockPuller.PullBlock(seq)\n\t\t\tif nextBlock == nil {\n\t\t\t\treturn n, errors.WithMessagef(cluster.ErrRetryCountExhausted, \"failed to pull block %d\", seq)\n\t\t\t}\n\n\t\t\treportedPrevHash := nextBlock.Header.PreviousHash\n\t\t\tif (nextBlock.Header.Number > 0) && !bytes.Equal(reportedPrevHash, actualPrevHash) {\n\t\t\t\treturn n, errors.Errorf(\"block header previous hash mismatch on sequence %d, expected %x, got %x\",\n\t\t\t\t\tnextBlock.Header.Number, actualPrevHash, reportedPrevHash)\n\t\t\t}\n\n\t\t\tif c.joinBlock != nil && c.joinBlock.Header.Number == nextBlock.Header.Number {\n\t\t\t\t// We don't need to verify the block.Data because we verify the join-block's DataHash against the\n\t\t\t\t// hash(join-block.Data) when we verify it during the `Join` REST API call\n\t\t\t\tif !proto.Equal(nextBlock.Header, c.joinBlock.Header) {\n\t\t\t\t\tc.logger.Errorf(\"Block header mismatch between the block we pulled and the block we joined with, sequence %d\", c.joinBlock.Header.Number)\n\t\t\t\t\treturn n, errors.Errorf(\"block header mismatch between the block we pulled and the block we joined with, sequence %d\", c.joinBlock.Header.Number)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tactualPrevHash = protoutil.BlockHeaderHash(nextBlock.Header)\n\t\t\tif err := c.ledgerResources.Append(nextBlock); err != nil {\n\t\t\t\treturn n, errors.WithMessagef(err, \"failed to append block %d to the ledger\", nextBlock.Header.Number)\n\t\t\t}\n\n\t\t\tif protoutil.IsConfigBlock(nextBlock) {","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/follower/follower_chain.go#L523-L559","documentation":"The follower pulled block `seq` from the ordering service and found its Header.PreviousHash does not equal the hash of the preceding block it verified. This is a hash-chain break between consecutively pulled blocks, so the follower refuses to append and returns ErrRetryCountExhausted-wrapped error. It protects against appending a block from a forked or tampered chain.","triggerScenarios":"pullUntilTarget pulls consecutive blocks and nextBlock.Header.Number > 0 with bytes.Equal(reportedPrevHash, actualPrevHash) false — the ordering service served a block whose PreviousHash does not match the previously appended block's hash (fork, tampering, or serving blocks from a different channel/consensus history).","commonSituations":"A malicious or misconfigured ordering node serving blocks from a divergent fork; a follower connected to the wrong ordering endpoint/channel; block bytes corrupted in transit without TLS/integrity protection; mixing snapshots from different network generations.","solutions":["Verify the follower is connected to the correct, trusted ordering endpoints (orderer endpoint list / cluster root CAs in the join request) and the intended channel.","Compare the offending block against other orderers' blocks; if forks are detected, investigate the consensus/raft state of the ordering service.","Re-join the follower with a clean ledger and correct join block so verification restarts from a known-good genesis/config block.","Enable TLS with proper root CAs to eliminate block corruption/MITM in transit, then retry pullUntilLatestWithRetry."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := pullUntilLatestWithRetry(...); err != nil { if strings.Contains(err.Error(), \"previous hash mismatch\") { quarantine follower; investigate ordering service fork } }","preventionTips":["Always use TLS with pinned cluster root CAs to the ordering service","Point followers only at official ordering endpoints for the channel","Monitor ordering service for raft/fork indicators","Re-join from a clean ledger after any re-genesis"],"tags":["orderer","fabric","blockchain-fork","hash-mismatch","security"],"backgroundTag":"block-hash-chain-break","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"}