thanos-io/thanos · error

got bad identifier

Error message

got bad identifier %s

What it means

The postings iterator reads a Snappy-framed postings blob and expects the stream to begin with the 6-byte magic body "sNaPpY". This error is set when the first 6 bytes of the current chunk do not match that magic string, meaning the input is not the expected Snappy-identifier chunk or the framing is desynchronized. The iterator records the error and stops, so Next() returns false and Err() returns this error.

Solutions

  1. Regenerate the postings data by re-compacting the block with `promtool tsdb create-blocks-from` or letting TSDB rewrite the block so a valid Snappy identifier chunk is written.
  2. Verify the buffer offset: make sure the slice passed to the decoder starts at the codec header, not shifted by earlier bytes.
  3. Check that source data is intact (checksum the block files); restore from a healthy replica or backup.
  4. Confirm client and server Prometheus versions agree on the postings codec (Snappy header) when sharing data.

Example fix

// before: decoding a slice that may not carry the snappy magic
it, err := NewPostingsCodecIterator(rawPostings)
// after: verify the header first
if !bytes.HasPrefix(rawPostings, []byte("sNaPpY")) {
    return nil, fmt.Errorf("postings blob lacks snappy header, len=%d", len(rawPostings))
}
it, err := NewPostingsCodecIterator(rawPostings)
Defensive patterns

Strategy: validation

Validate before calling

if !bytes.HasPrefix(blob, []byte("sNaPpY")) {
    return fmt.Errorf("postings blob missing snappy magic header")
}

Type guard

func hasSnappyMagic(b []byte) bool { return bytes.HasPrefix(b, []byte("sNaPpY")) }

Prevention

When it happens

Trigger: Calling Next() on a postings iterator whose underlying byte slice does not start with the "sNaPpY" magic identifier; e.g. decoding data produced by another codec version, reading a truncated/shifted buffer, or passing raw uncompressed postings to diffVarintSnappyDecode-style iteration.

Common situations: Reading a postings file written by an older Prometheus version (pre-Snappy header), corruption during block download/repair, manually concatenated or offset buffers, or feeding non-postings binary data into the postings decoder.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at pkg/store/postings_codec.go:257

	it.input = it.input[1:]

	if len(it.input) < 3 {
		it.err = io.ErrUnexpectedEOF
		return false
	}

	chunkLen := int(it.input[0]) | int(it.input[1])<<8 | int(it.input[2])<<16
	it.input = it.input[3:]

	switch chunkType {
	case chunkTypeStreamIdentifier:
		const magicBody = "sNaPpY"
		if chunkLen != len(magicBody) {
			it.err = fmt.Errorf("corrupted identifier")
			return false
		}
		if string(it.input[:len(magicBody)]) != magicBody {
			it.err = fmt.Errorf("got bad identifier %s", string(it.input[:6]))
			return false
		}
		it.input = it.input[6:]
		it.readSnappyIdentifier = true
		return it.readNextChunk(nil)
	case chunkTypeCompressedData:
		if !it.readSnappyIdentifier {
			it.err = fmt.Errorf("missing magic snappy marker")
			return false
		}
		if len(it.input) < 4 {
			it.err = io.ErrUnexpectedEOF
			return false
		}
		checksum := uint32(it.input[0]) | uint32(it.input[1])<<8 | uint32(it.input[2])<<16 | uint32(it.input[3])<<24
		if len(it.input) < chunkLen {
			it.err = io.ErrUnexpectedEOF
			return false

View on GitHub (pinned to 35b8b99117)