nats-io/nats-server · error
error reading compression reader: %w
Error message
error reading compression reader: %w
What it means
This error is returned by LocalCache.Decompress when reading from the s2 reader fails during io.ReadAll, meaning the cached OCSP response bytes are not valid s2-compressed data or are corrupted. It is wrapped with certidp.ErrCannotReadCompressed and surfaced to Get callers so the corrupt entry can be treated as a miss rather than served.
Source
Thrown at server/ocsp_responsecache.go:366
input := bytes.NewReader(buf[:bodyLen])
if n, err := io.CopyN(writer, input, bodyLen); err != nil {
return nil, fmt.Errorf(certidp.ErrCannotWriteCompressed, err)
} else if n != bodyLen {
return nil, fmt.Errorf(certidp.ErrTruncatedWrite, n, bodyLen)
}
if err := writer.Close(); err != nil {
return nil, fmt.Errorf(certidp.ErrCannotCloseWriter, err)
}
return output.Bytes(), nil
}
func (c *LocalCache) Decompress(buf []byte) ([]byte, error) {
bodyLen := int64(len(buf))
input := bytes.NewReader(buf[:bodyLen])
reader := io.NopCloser(s2.NewReader(input))
output, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf(certidp.ErrCannotReadCompressed, err)
}
return output, reader.Close()
}
func (c *LocalCache) loadCache(s *Server) {
d := s.opts.OCSPCacheConfig.LocalStore
if d == _EMPTY_ {
d = OCSPResponseCacheDefaultDir
}
f := OCSPResponseCacheDefaultFilename
store, err := filepath.Abs(path.Join(d, f))
if err != nil {
s.Errorf(certidp.ErrLoadCacheFail, err)
return
}
s.Debugf(certidp.DbgLoadingCache, store)
c.mu.Lock()
defer c.mu.Unlock()View on GitHub (pinned to 3a66a489d2)
Solutions
- Delete the corrupt entry (or the whole local cache directory) so Get re-fetches and re-compresses a fresh response.
- Verify entries were written by the same Compress implementation/format version that Decompress expects.
- Confirm the on-disk file length matches the recorded bodyLen; re-store if truncated.
- Log the wrapped s2 error to identify whether it is a header or checksum failure before retrying.
Example fix
// before
output, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf(certidp.ErrCannotReadCompressed, err)
}
// after
output, err := io.ReadAll(reader)
if err != nil {
// treat as cache miss so caller re-fetches instead of failing
return nil, fmt.Errorf("%w: %v (corrupt cache entry, evicting)", certidp.ErrCannotReadCompressed, err)
} Defensive patterns
Strategy: fallback
Validate before calling
// on Get: detect non-s2 data before Decompress
if n, ok := s2.DecodedLen(buf); !ok || n <= 0 {
return nil, ErrCorruptCacheEntry
} Try / catch
data, err := cache.Decompress(raw)
if err != nil {
log.Printf("corrupt cache entry, refetching: %v", err)
return fetchAndStore() // fallback to origin
} Prevention
- Evict and refetch on any Decompress error instead of serving the entry.
- Keep the Compress/Decompress format version in the entry header.
- Periodically validate cache files on disk for truncation.
When it happens
Trigger: LocalCache.Get -> Decompress where the stored bytes were not produced by Compress, were truncated on disk, or the s2 stream has a bad checksum/block header causing io.ReadAll to fail.
Common situations: Manual edits or corruption of the local cache directory; entries written by a different compression format/version; disk truncation after a crash; a plaintext response loaded into a compressed cache slot.
Related errors
- short write on body (%d != %d)
- error closing compression writer: %w
- failed to load block from disk: %w
- failed to decompress block: %w
- metadata incomplete
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/1b7a0f33932f02fa.
Report an issue: GitHub.