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
- 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.
- Verify the buffer offset: make sure the slice passed to the decoder starts at the codec header, not shifted by earlier bytes.
- Check that source data is intact (checksum the block files); restore from a healthy replica or backup.
- 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
- Never slice off leading bytes of a postings blob before decoding.
- Verify block checksums after download or copy.
- Keep Prometheus versions consistent when sharing block data.
- Regenerate old-format blocks with promtool before reading with new code.
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
- missing magic snappy marker
- mismatched checksum (got , expected )
- unsupported chunk type
- invalid chunk type
- snappy decode
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 falseView on GitHub (pinned to 35b8b99117)