thanos-io/thanos · error

preloaded chunk too small, expecting

Error message

preloaded chunk too small, expecting %d

What it means

A chunk served from the object store returned fewer bytes than the index says the chunk occupies (chunkDataLen < chunkLen). The preloaded part fetch did not cover the whole chunk, so the reader falls back to readChunkRange to fetch the missing bytes. It fires when the block's index and chunk data are inconsistent (truncated or corrupted object) or when the preload range was computed too small.

Solutions

  1. Let the fallback readChunkRange fetch the full chunk; only act if that also fails
  2. Verify block health with 'thanos tools bucket verify' and mark or repair broken blocks
  3. Check the object store for truncated uploads/parallel writes to the block's chunks files
  4. If persistent, remove and re-upload the affected block from its source
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at pkg/store/bucket.go:3820 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/1591ba250267ec88. Report an issue: GitHub.

Appendix: source

Thrown at pkg/store/bucket.go:3820

			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()
		// Read entire chunk into new buffer.
		// TODO: readChunkRange call could be avoided for any chunk but last in this particular part.
		if err := bytesLimiter.ReserveWithType(uint64(chunkLen), ChunksTouched); err != nil {
			return httpgrpc.Errorf(int(codes.ResourceExhausted), "exceeded bytes limit while fetching chunks: %s", err)
		}

		nb, err := r.block.readChunkRange(ctx, seq, int64(pIdx.offset), int64(chunkLen), []byteRange{{offset: 0, length: chunkLen}}, r.logger)
		if err != nil {
			return errors.Wrapf(err, "preloaded chunk too small, expecting %d, and failed to fetch full chunk", chunkLen)
		}
		if len(*nb) != chunkLen {
			return errors.Errorf("preloaded chunk too small, expecting %d", chunkLen)
		}

		stats.add(ChunksFetched, 1, len(*nb))
		c := rawChunk((*nb)[n:])
		err = populateChunk(&(res[pIdx.seriesEntry].chks[pIdx.chunk]), &c, aggrs, r.save, calculateChunkChecksum)
		if err != nil {
			r.block.chunkPool.Put(nb)
			return errors.Wrap(err, "populate chunk")
		}

		stats.add(ChunksTouched, 1, int(chunkDataLen))

		r.block.chunkPool.Put(nb)
	}
	return nil
}

// save saves a copy of b's payload to a memory pool of its own and returns a new byte slice referencing said copy.

View on GitHub (pinned to 35b8b99117)