{"record":{"id":"e293c2b5c6bf131b","repo":"dgraph-io/badger","slug":"block-out-of-index","errorCode":null,"errorMessage":"block out of index","messagePattern":"block out of index","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"table/table.go","lineNumber":555,"sourceCode":"\t}\n\n\tindex, err := t.readTableIndex()\n\ty.Check(err)\n\tt.opt.IndexCache.Set(t.indexKey(), index, int64(t.indexLen))\n\treturn index\n}\n\nfunc (t *Table) offsets(ko *fb.BlockOffset, i int) bool {\n\treturn t.fetchIndex().Offsets(ko, i)\n}\n\n// block function return a new block. Each block holds a ref and the byte\n// slice stored in the block will be reused when the ref becomes zero. The\n// caller should release the block by calling block.decrRef() on it.\nfunc (t *Table) block(idx int, useCache bool) (*Block, error) {\n\ty.AssertTruef(idx >= 0, \"idx=%d\", idx)\n\tif idx >= t.offsetsLength() {\n\t\treturn nil, errors.New(\"block out of index\")\n\t}\n\tif t.opt.BlockCache != nil {\n\t\tkey := t.blockCacheKey(idx)\n\t\tblk, ok := t.opt.BlockCache.Get(key)\n\t\tif ok && blk != nil {\n\t\t\t// Use the block only if the increment was successful. The block\n\t\t\t// could get evicted from the cache between the Get() call and the\n\t\t\t// incrRef() call.\n\t\t\tif blk.incrRef() {\n\t\t\t\treturn blk, nil\n\t\t\t}\n\t\t}\n\t}\n\n\tvar ko fb.BlockOffset\n\ty.AssertTrue(t.offsets(&ko, idx))\n\tblk := &Block{offset: int(ko.Offset())}\n\tblk.ref.Store(1)","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/table/table.go#L537-L573","documentation":"Table.block returns this error when the requested block index is >= the number of block offsets in the table index (negative is asserted separately). It means the caller asked for a block that does not exist in this table.","triggerScenarios":"Iterator code (seekToFirst, seekToLast, seekHelper, next, prev) or VerifyChecksum computing a block index from offsets and running past the last block — usually due to a stale/corrupt index, concurrent table deletion, or an out-of-range idx from binary search over corrupted offsets.","commonSituations":"Corrupted index lengths from damaged SSTs; race between iterators and table cleanup (DropAll/compaction) in older versions; custom code indexing t.offsets() directly.","solutions":["Guard the block index against offsetsLength() before calling t.block(idx, ...)","Upgrade Badger — races between iteration and table dropping were fixed over time","If it appears during open, treat the table as corrupt and restore it from backup","Check that no custom code calls block() with hand-computed indices"],"exampleFix":"// before\nblk, err := t.block(idx, true)\n\n// after\nif idx >= t.offsetsLength() {\n    return io.EOF // or handle gracefully\n}\nblk, err := t.block(idx, true)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func (t *table) hasBlock(idx int) bool {\n    return idx >= 0 && idx < t.offsetsLength()\n}\n// call site: if !hasBlock(idx) { return io.EOF }","tryCatchPattern":"blk, err := tbl.block(idx, true)\nif errors.Is(err, errBlockOutOfIndex) || err != nil && err.Error() == \"block out of index\" {\n    return io.EOF // end iteration gracefully\n}","preventionTips":["Always bounds-check block indices against offsetsLength()","Upgrade Badger to pick up iterator/cleanup race fixes","Avoid custom code that indexes table blocks directly"],"tags":["iterator","index","bounds","storage"],"backgroundTag":"block-out-of-index","analyzedSha":"2a001d466f6b71a917319a1db41f99860e16e269","analyzedAt":"2026-09-05T13:00:02.264Z","contentChangedAt":"2026-09-05T13:00:02.264Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}