{"record":{"id":"e00175c8b2eac6fd","repo":"thanos-io/thanos","slug":"decode-postings","errorCode":null,"errorMessage":"decode postings","messagePattern":"decode postings","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/store/bucket.go","lineNumber":3150,"sourceCode":"\t\treturn nil, closeFns, httpgrpc.Errorf(int(codes.ResourceExhausted), \"exceeded bytes limit while loading postings from index cache: %s\", err)\n\t}\n\n\t// Iterate over all groups and fetch posting from cache.\n\t// If we have a miss, mark key to be fetched in `ptrs` slice.\n\t// Overlaps are well handled by partitioner, so we don't need to deduplicate keys.\n\tfor ix, key := range keys {\n\t\tif (ix+1)%checkContextEveryNIterations == 0 {\n\t\t\tif err := ctx.Err(); err != nil {\n\t\t\t\treturn nil, closeFns, err\n\t\t\t}\n\t\t}\n\t\t// Get postings for the given key from cache first.\n\t\tif b, ok := fromCache[key]; ok {\n\t\t\tr.stats.add(PostingsTouched, 1, len(b))\n\n\t\t\tl, closer, err := r.decodeCachedPostings(b)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, closeFns, errors.Wrap(err, \"decode postings\")\n\t\t\t}\n\t\t\toutput[ix] = l\n\t\t\tcloseFns = append(closeFns, closer...)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Cache miss; save pointer for actual posting in index stored in object store.\n\t\tptr, err := r.block.indexHeaderReader.PostingsOffset(key.Name, key.Value)\n\t\tif err == indexheader.NotFoundRangeErr {\n\t\t\t// This block does not have any posting for given key.\n\t\t\toutput[ix] = index.EmptyPostings()\n\t\t\tcontinue\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, closeFns, errors.Wrap(err, \"index header PostingsOffset\")\n\t\t}\n","sourceCodeStart":3132,"sourceCodeEnd":3168,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/store/bucket.go#L3132-L3168","documentation":"bucketIndexReader.Postings failed to decode a cached postings blob fetched from the block index cache. The cache stores snappy-compressed diff-varint postings, and decodeCachedPostings snappy-decodes and parses them back into a Postings list. This error means the cached bytes are unreadable/corrupt or were written by an incompatible format version, so the reader cannot serve postings from cache.","triggerScenarios":"r.block.indexCache.FetchPostings returned bytes for a key and r.decodeCachedPostings(b) returned an error: corrupted cache entry in the shared store/index cache, cache entry produced by a different Thanos version or encoding scheme (e.g. plain snappy vs streamed snappy, or future cachedPostingsVersion), or truncated cache payload.","commonSituations":"Upgrading Thanos while an object-storage or in-memory index cache still holds entries encoded by the old version; a buggy or misconfigured cache backend (e.g. Redis/Memcached with evictions, truncation, or binary-corrupting serialization); manual tampering with cached blobs.","solutions":["Evict the affected postings cache entries (flush or restart the cache) so postings are re-fetched and re-encoded from the index","Verify all Thanos store-gateway instances run the same version so cache encoding is consistent","Disable or clear the external postings cache (store.index-cache.posts config) and re-enable after upgrade","Check the cache backend for binary-unsafe serialization or size limits that truncate values"],"exampleFix":"// before\nl, closer, err := r.decodeCachedPostings(b)\nif err != nil {\n\treturn nil, closeFns, errors.Wrap(err, \"decode postings\")\n}\n// after\nl, closer, err := r.decodeCachedPostings(b)\nif err != nil {\n\tlevel.Warn(r.logger).Log(\"msg\", \"failed to decode cached postings, falling back to fetch\", \"err\", err)\n\tr.block.indexCache.StorePostings(...) // invalidate/overwrite stale entry\n\t// fall through to remote fetch path instead of failing the query\n}","handlingStrategy":"fallback","validationCode":"if len(cached) == 0 || !bytes.HasPrefix(cached, snappyStreamedMagic) {\n\t// treat as cache miss: fall back to fetching postings from object storage\n}","typeGuard":"func isValidCachedPostings(b []byte) bool { return len(b) > 0 && isSnappyStreamed(b) }","tryCatchPattern":"l, closer, err := r.decodeCachedPostings(b)\nif err != nil {\n\tlogger.Warn(\"cached postings undecodable; refetching from index\", \"err\", err)\n\tl, closer, err = fetchPostingsFromIndex(ctx, key) // fallback path\n\tif err != nil { return nil, closeFns, err }\n}","preventionTips":["Flush the postings index cache across Thanos upgrades","Use a cache backend that preserves binary payloads intact","Set sane cache entry size limits to avoid truncation","Monitor cachedPostingsCompressionErrors/compression version mismatches"],"tags":["thanos","cache","snappy","decoding","index"],"backgroundTag":"cached-postings-decode-failed","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}