From dcda4840a70ac6f3f29c82b0e7947c4586537a41 Mon Sep 17 00:00:00 2001
From: Patryk Prus
Date: Tue, 6 Jan 2026 08:07:23 -0500
Subject: [PATCH] tsdb/index: export sentinel errors for size limit failures
(#17773)
* tsdb/index: export sentinel errors for size limit failures
---------
Signed-off-by: Patryk Prus
Co-authored-by: Arve Knudsen
---
tsdb/index/index.go | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/tsdb/index/index.go b/tsdb/index/index.go
index 1ddcac9501..8a76770821 100644
--- a/tsdb/index/index.go
+++ b/tsdb/index/index.go
@@ -17,6 +17,7 @@ import (
"bufio"
"context"
"encoding/binary"
+ "errors"
"fmt"
"hash"
"hash/crc32"
@@ -94,6 +95,13 @@ func (s indexWriterStage) String() string {
return ""
}
+// ErrPostingsOffsetTableTooLarge is returned when the postings offset table length
+// would exceed 4 bytes (table would exceed the 4GB limit).
+var ErrPostingsOffsetTableTooLarge = errors.New("length size exceeds 4 bytes")
+
+// ErrIndexExceeds64GiB is returned when the index file would exceed the 64GiB limit.
+var ErrIndexExceeds64GiB = errors.New("exceeding max size of 64GiB")
+
// The table gets initialized with sync.Once but may still cause a race
// with any other use of the crc32 package anywhere. Thus we initialize it
// before.
@@ -303,7 +311,7 @@ func (fw *FileWriter) Write(bufs ...[]byte) error {
// Once we move to compressed/varint representations in those areas, this limitation
// can be lifted.
if fw.pos > 16*math.MaxUint32 {
- return fmt.Errorf("%q exceeding max size of 64GiB", fw.name)
+ return fmt.Errorf("%q %w", fw.name, ErrIndexExceeds64GiB)
}
}
return nil
@@ -660,7 +668,7 @@ func (w *Writer) writeLengthAndHash(startPos uint64) error {
w.buf1.Reset()
l := w.f.pos - startPos - 4
if l > math.MaxUint32 {
- return fmt.Errorf("length size exceeds 4 bytes: %d", l)
+ return fmt.Errorf("%w: %d", ErrPostingsOffsetTableTooLarge, l)
}
w.buf1.PutBE32int(int(l))
if err := w.writeAt(w.buf1.Get(), startPos); err != nil {