thanos-io/thanos · error

reading chunk length failed

Error message

reading chunk length failed

What it means

Raised in bucketChunkReader.load when binary.Uvarint cannot decode the chunk's length header from the fetched bytes (n < 1). The bytes at the expected chunk offset are not a valid chunk header, indicating the fetched range is misaligned or the block data is corrupted/truncated.

Solutions

  1. Verify block/chunks integrity in object storage (checksums, complete upload)
  2. Re-download the affected chunk range; check for truncated range reads
  3. Confirm index offsets match the chunks file (no out-of-sync block files)
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pkg/store/bucket.go:3790 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at pkg/store/bucket.go:3790

		// However, declaration for EstimatedMaxChunkSize warns us some chunks could be larger in some rare cases.
		// This is handled further down below.
		chunkLen = r.block.estimatedMaxChunkSize
		if i+1 < len(pIdxs) {
			if diff = pIdxs[i+1].offset - pIdx.offset; int(diff) < chunkLen {
				chunkLen = int(diff)
			}
		}
		cb := buf[:chunkLen]
		n, err = io.ReadFull(bufReader, cb)
		readOffset += n
		// Unexpected EOF for last chunk could be a valid case. Any other errors are definitely real.
		if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) && i != len(pIdxs)-1 {
			return errors.Wrapf(err, "read range for seq %d offset %x", seq, pIdx.offset)
		}

		chunkDataLen, n := binary.Uvarint(cb)
		if n < 1 {
			return errors.New("reading chunk length failed")
		}

		// Chunk length is n (number of bytes used to encode chunk data), 1 for chunk encoding and chunkDataLen for actual chunk data.
		// There is also crc32 after the chunk, but we ignore that.
		chunkLen = n + 1 + int(chunkDataLen)
		if chunkLen <= len(cb) {
			c := rawChunk(cb[n:chunkLen])
			err = populateChunk(&(res[pIdx.seriesEntry].chks[pIdx.chunk]), &c, aggrs, r.save, calculateChunkChecksum)
			if err != nil {
				return errors.Wrap(err, "populate chunk")
			}
			stats.add(ChunksTouched, 1, int(chunkDataLen))
			continue
		}

		r.block.metrics.chunkRefetches.WithLabelValues(tenant).Inc()
		// If we didn't fetch enough data for the chunk, fetch more.
		fetchBegin = time.Now()

View on GitHub (pinned to 35b8b99117)