thanos-io/thanos · error

snappy decode

Error message

snappy decode

What it means

The decoder calls s2.Decode to decompress the postings body and wraps any decode failure in an error with the message "snappy decode". This means the compressed payload after the codec header is not valid Snappy/s2 data — truncated, corrupted, or not Snappy-compressed at all.

Solutions

  1. Re-copy or re-download the affected block/section and retry; verify checksums to rule out transfer corruption.
  2. Confirm the slice offset: input must be the full postings blob including the codec header, with s2 payload starting at input[len(codecHeaderSnappy):].
  3. Run `promtool check blocks` to identify corrupt blocks and remove/restore them.
  4. Check storage integrity (disk SMART, filesystem) if corruption recurs.

Example fix

// before
raw, err := diffVarintSnappyDecode(input[3:], false) // misaligned slice
// after
raw, err := diffVarintSnappyDecode(input, false) // full blob incl. header
Defensive patterns

Strategy: try-catch

Validate before calling

if len(input) <= len(codecHeaderSnappy) {
    return errors.New("postings payload too short to decode")
}

Try / catch

p, err := diffVarintSnappyDecode(input, false)
if err != nil {
    var s2Err s2Error
    if errors.As(err, &s2Err) || strings.Contains(err.Error(), "snappy decode") {
        // payload corrupt/truncated: refetch block
    }
}

Prevention

When it happens

Trigger: diffVarintSnappyDecode receives input whose body fails s2.Decode: corrupted bytes, truncated buffer, wrong slice start (skipping the header but keeping misaligned payload), or data compressed with an incompatible setting.

Common situations: Corrupted block files on disk, incomplete downloads/copies, byte-offset mistakes when slicing the index section, or mixing formats between Prometheus versions.

Related errors


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

Appendix: source

Thrown at pkg/store/postings_codec.go:486

	if !isDiffVarintSnappyEncodedPostings(input) {
		return nil, errors.New("header not found")
	}

	toFree := make([][]byte, 0, 2)

	var dstBuf []byte
	if !disablePooling {
		if len, err := s2.DecodedLen(input[len(codecHeaderSnappy):]); err == nil {
			if decodeBuf, err := snappyDecodePool.Get(len); err == nil && decodeBuf != nil {
				dstBuf = *decodeBuf
				toFree = append(toFree, dstBuf)
			}
		}
	}

	raw, err := s2.Decode(dstBuf, input[len(codecHeaderSnappy):])
	if err != nil {
		return nil, errors.Wrap(err, "snappy decode")
	}

	if !alias(raw, dstBuf) && !disablePooling {
		toFree = append(toFree, raw)
	}

	return newDiffVarintPostings(raw, toFree), nil
}

func newDiffVarintPostings(input []byte, freeSlices [][]byte) *diffVarintPostings {
	return &diffVarintPostings{freeSlices: freeSlices, buf: &encoding.Decbuf{B: input}}
}

// diffVarintPostings is an implementation of index.Postings based on diff+varint encoded data.
type diffVarintPostings struct {
	buf        *encoding.Decbuf
	cur        storage.SeriesRef
	freeSlices [][]byte

View on GitHub (pinned to 35b8b99117)