{"record":{"id":"3b42421e2ff7bae6","repo":"hyperledger/fabric","slug":"envelope-index-out-of-bounds","errorCode":null,"errorMessage":"envelope index out of bounds","messagePattern":"envelope index out of bounds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"protoutil/commonutils.go","lineNumber":102,"sourceCode":"// and unmarshals it -- it panics if either of these operations fail\nfunc ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope {\n\tenvelope, err := ExtractEnvelope(block, index)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn envelope\n}\n\n// ExtractEnvelope retrieves the requested envelope from a given block and\n// unmarshals it\nfunc ExtractEnvelope(block *cb.Block, index int) (*cb.Envelope, error) {\n\tif block.Data == nil {\n\t\treturn nil, errors.New(\"block data is nil\")\n\t}\n\n\tenvelopeCount := len(block.Data.Data)\n\tif index < 0 || index >= envelopeCount {\n\t\treturn nil, errors.New(\"envelope index out of bounds\")\n\t}\n\tmarshaledEnvelope := block.Data.Data[index]\n\tenvelope, err := GetEnvelopeFromBlock(marshaledEnvelope)\n\terr = errors.WithMessagef(err, \"block data does not carry an envelope at index %d\", index)\n\treturn envelope, err\n}\n\n// MakeChannelHeader creates a ChannelHeader.\nfunc MakeChannelHeader(headerType cb.HeaderType, version int32, chainID string, epoch uint64) *cb.ChannelHeader {\n\ttm := timestamppb.Now()\n\ttm.Nanos = 0\n\treturn &cb.ChannelHeader{\n\t\tType:      int32(headerType),\n\t\tVersion:   version,\n\t\tTimestamp: tm,\n\t\tChannelId: chainID,\n\t\tEpoch:     epoch,\n\t}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/protoutil/commonutils.go#L84-L120","documentation":"ExtractEnvelope returns this error when the requested index is negative or >= the number of envelopes in block.Data.Data, i.e. the caller asked for a transaction slot that does not exist in the block. Each block only carries envelopeCount entries.","triggerScenarios":"Calling ExtractEnvelope(block, index) with index < 0 or index >= len(block.Data.Data) — typically iterating with a wrong bound, assuming a fixed number of txs per block, or using an index from another block.","commonSituations":"Block iteration code using the configured batch size instead of len(block.Data.Data); validators/committers re-processing a block after metadata mismatch; tests passing index 0 on an empty block; duplicated-tx lookup (TestBlockfileMgrGetTxByIdDuplicateTxid) with stale indices.","solutions":["Bound your loop with len(block.Data.Data) rather than a fixed tx count or block batch size.","Check the index source: tx validation codes/txids indexes must be recomputed if they reference indices beyond the block.","Use GetTransactionByID/ledger index APIs instead of hand-tracking envelope indices.","For a zero-transaction block, handle envelopeCount == 0 explicitly before extracting."],"exampleFix":"// before\nfor i := 0; i < blockSize; i++ { // blockSize from config, may exceed actual\n    env, err := protoutil.ExtractEnvelope(block, i)\n}\n// after\nfor i := 0; i < len(block.Data.Data); i++ {\n    env, err := protoutil.ExtractEnvelope(block, i)\n}","handlingStrategy":"validation","validationCode":"func envelopeCount(block *cb.Block) int {\n    if block == nil || block.Data == nil {\n        return 0\n    }\n    return len(block.Data.Data)\n}\n// before extracting: if index < 0 || index >= envelopeCount(block) { skip }","typeGuard":"func validEnvelopeIndex(block *cb.Block, index int) bool {\n    return block != nil && block.Data != nil && index >= 0 && index < len(block.Data.Data)\n}","tryCatchPattern":"env, err := protoutil.ExtractEnvelope(block, i)\nif err != nil {\n    if strings.Contains(err.Error(), \"envelope index out of bounds\") {\n        logger.Warnf(\"block %d: index %d out of bounds (%d txs); truncating iteration\",\n            block.GetHeader().GetNumber(), i, len(block.Data.GetData()))\n        break\n    }\n    return err\n}","preventionTips":["Iterate with len(block.Data.Data), never a configured batch size or assumed tx count","Handle empty blocks (0 envelopes) explicitly before extraction","Recompute tx validation/index metadata if stored indices drift from block contents","Prefer ledger APIs (GetTransactionByID) over manual envelope indexing"],"tags":["hyperledger-fabric","index-out-of-bounds","block-processing"],"backgroundTag":"index-out-of-bounds","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"}