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

  1. Delete the corrupt entry (or the whole local cache directory) so Get re-fetches and re-compresses a fresh response.
  2. Verify entries were written by the same Compress implementation/format version that Decompress expects.
  3. Confirm the on-disk file length matches the recorded bodyLen; re-store if truncated.
  4. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/1b7a0f33932f02fa. Report an issue: GitHub.