{"record":{"id":"ffbf7b93416ddf59","repo":"hyperledger/fabric","slug":"expected-block-number-is-d-but-actual-block-num","errorCode":null,"errorMessage":"expected block number is [%d] but actual block number inside block is [%d]","messagePattern":"expected block number is \\[(.+?)\\] but actual block number inside block is \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/deliverclient/block_verification.go","lineNumber":321,"sourceCode":"func (a *BlockVerificationAssistant) verifyMetadata(block *common.Block) error {\n\tif block.Metadata == nil || len(block.Metadata.Metadata) < len(common.BlockMetadataIndex_name) {\n\t\treturn errors.Errorf(\"block with id [%d] on channel [%s] does not have metadata or contains too few entries\", block.Header.Number, a.channelID)\n\t}\n\n\treturn nil\n}\n\nfunc (a *BlockVerificationAssistant) verifyHeader(block *common.Block) error {\n\tif block == nil {\n\t\treturn errors.Errorf(\"block must be different from nil, channel=%s\", a.channelID)\n\t}\n\tif block.Header == nil {\n\t\treturn errors.Errorf(\"invalid block, header must be different from nil, channel=%s\", a.channelID)\n\t}\n\n\texpectedBlockNum := a.lastBlockHeader.Number + 1\n\tif expectedBlockNum != block.Header.Number {\n\t\treturn errors.Errorf(\"expected block number is [%d] but actual block number inside block is [%d]\", expectedBlockNum, block.Header.Number)\n\t}\n\n\tif len(a.lastBlockHeaderHash) != 0 {\n\t\tif !bytes.Equal(block.Header.PreviousHash, a.lastBlockHeaderHash) {\n\t\t\treturn errors.Errorf(\"Header.PreviousHash of block [%d] is different from Hash(block.Header) of previous block, on channel [%s], received: %s, expected: %s\",\n\t\t\t\tblock.Header.Number, a.channelID, hex.EncodeToString(block.Header.PreviousHash), hex.EncodeToString(a.lastBlockHeaderHash))\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":303,"sourceCodeEnd":332,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/deliverclient/block_verification.go#L303-L332","documentation":"verifyHeader expects the next block to be exactly lastBlockHeader.Number + 1. A delivered block whose Header.Number does not match the expected sequence breaks the assumption of a contiguous ledger stream, so the assistant rejects it. This protects the client against gaps, replays, and out-of-order delivery.","triggerScenarios":"VerifyBlock/VerifyBlockAttestation receives a block whose Header.Number != a.lastBlockHeader.Number + 1 — e.g. a duplicate of the last block, a skipped block, or a block from an earlier point in the ledger arriving on the deliver stream.","commonSituations":"Orderer restarted/resynced and re-delivered old blocks; deliver client reconnected without resetting lastBlockHeader; Seeking a start block that overlaps already-received blocks; multiple orderers in a cluster out of sync delivering divergent sequences.","solutions":["Check whether the block is a duplicate; if so, skip it instead of re-verifying against the stale lastBlockHeader.","Reset or re-seed the BlockVerificationAssistant's lastBlockHeader to the correct ledger height after reconnects.","Adjust the SeekInfo start position in the deliver request so the stream begins at the client's actual ledger height.","Compare orderer ledger heights across ordering nodes; resync an orderer that is behind or ahead."],"exampleFix":"// before\nenv, _ := protoutil.CreateDeliverEnvelope(..., &ab.SeekPosition{Type: &ab.SeekPosition_Newest{}}) // mismatch with local height\n// after\nstartFrom := localLedgerHeight // e.g. fetched from ledger\nenv, _ := protoutil.CreateDeliverEnvelope(..., &ab.SeekPosition{Type: &ab.SeekPosition_Specified{\n    Specified: &ab.SeekSpecified{Start: startFrom},\n}})","handlingStrategy":"retry","validationCode":"if blk.Header.Number != lastBlockHeader.Number+1 {\n    return fmt.Errorf(\"unexpected block %d; expected %d — re-seek from correct height\", blk.Header.Number, lastBlockHeader.Number+1)\n}","typeGuard":null,"tryCatchPattern":"if err := assistant.VerifyBlock(blk); err != nil {\n    var expected uint64\n    if n, e := fmt.Sscanf(err.Error(), \"expected block number is [%d]\", &expected); e == nil && n == 1 {\n        // reconnect the deliver stream seeking from `expected`\n    }\n}","preventionTips":["Seek deliver streams from your actual local ledger height, not Newest/oldest guesses.","Re-seed lastBlockHeader after every reconnect.","Track duplicates explicitly and skip already-seen block numbers."],"tags":["fabric","deliver-client","block-verification","sequence-mismatch"],"backgroundTag":"block-sequence-gap","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"}