thanos-io/thanos · error

unknown segment file for index

Error message

unknown segment file for index %d

What it means

bucketBlock.readChunkRange validates the segment index against the block's chunkObjs list before reading. If seq is negative or >= len(chunkObjs), meaning the caller asked for a segment file that does not exist in this block's index-header/meta, it returns 'unknown segment file for index %d'. It signals a caller/contract violation or a stale block view, not a storage failure.

Solutions

  1. Reload block metadata (remove stale cached blocks via store-gateway reload or restart) so chunkObjs matches the actual bucket state.
  2. Verify the index header file for the block lists the expected segment; re-download or use Thanos tools to inspect.
  3. Check for concurrent block deletion/compaction racing with reads; ensure compactor retention/cleanup is not removing blocks mid-query.
  4. If reproducible on a healthy block, file a bug with the block ID and query; it indicates internal index math went wrong.
Defensive patterns

Strategy: validation

Validate before calling

if seq < 0 || seq >= len(block.ChunkObjs()) {
	// refresh block metadata before retrying
	err := block.Reload(ctx)
}

Type guard

func validSegment(seq int, objs []string) bool { return seq >= 0 && seq < len(objs) }

Try / catch

if err != nil && strings.Contains(err.Error(), "unknown segment file") {
	// reload block list / resync store-gateway before retry
}

Prevention

When it happens

Trigger: Calling readChunkRange with a seq outside [0, len(b.chunkObjs)); happens when a cached ChunkObj/AggrChunk reference points to a segment that is no longer listed in the block's index header, or bad partition arithmetic on compound chunks.

Common situations: Store-gateway caching series/chunks across block reloads, corrupted or partially-uploaded index header, blocks replaced/deleted in the bucket while cached references persist, bugs in external tools computing chunk offsets.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/3bd0077379f2e511. Report an issue: GitHub.

Appendix: source

Thrown at pkg/store/bucket.go:2529

	if err != nil {
		return nil, errors.Wrap(err, "get range reader")
	}
	defer runutil.CloseWithLogOnErr(logger, r, "readIndexRange close range reader")

	// Preallocate the buffer with the exact size so we don't waste allocations
	// while progressively growing an initial small buffer. The buffer capacity
	// is increased by MinRead to avoid extra allocations due to how ReadFrom()
	// internally works.
	buf := bytes.NewBuffer(make([]byte, 0, length+bytes.MinRead))
	if _, err := buf.ReadFrom(r); err != nil {
		return nil, errors.Wrap(err, "read range")
	}
	return buf.Bytes(), nil
}

func (b *bucketBlock) readChunkRange(ctx context.Context, seq int, off, length int64, chunkRanges byteRanges, logger log.Logger) (*[]byte, error) {
	if seq < 0 || seq >= len(b.chunkObjs) {
		return nil, errors.Errorf("unknown segment file for index %d", seq)
	}

	// Get a reader for the required range.
	reader, err := b.bkt.GetRange(ctx, b.chunkObjs[seq], off, length)
	if err != nil {
		return nil, errors.Wrap(err, "get range reader")
	}
	defer runutil.CloseWithLogOnErr(logger, reader, "readChunkRange close range reader")

	// Get a buffer from the pool.
	chunkBuffer, err := b.chunkPool.Get(chunkRanges.size())
	if err != nil {
		return nil, errors.Wrap(err, "allocate chunk bytes")
	}

	*chunkBuffer, err = readByteRanges(reader, *chunkBuffer, chunkRanges)
	if err != nil {
		return nil, err

View on GitHub (pinned to 35b8b99117)