nats-io/nats-server · error

error closing compression writer: %w

Error message

error closing compression writer: %w

What it means

This error wraps a failure to Close the s2 compression writer in LocalCache.Compress. s2 writers buffer data and only flush the final compressed block on Close, so a Close error means the compressed output is incomplete/unusable. The library refuses to return bytes that could be a corrupt cache entry.

Source

Thrown at server/ocsp_responsecache.go:355

			c.stats.Revokes++
		case ocsp.Unknown:
			c.stats.Unknowns++
		}
	}
}

func (c *LocalCache) Compress(buf []byte) ([]byte, error) {
	bodyLen := int64(len(buf))
	var output bytes.Buffer
	writer := s2.NewWriter(&output)
	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_ {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check the error returned by writer.Close() and discard output.Bytes() — do not cache partial output.
  2. Ensure Close is called exactly once per writer and after all writes complete.
  3. Retry the compression of the same body once; s2 over bytes.Buffer should be deterministic.
  4. If reproducible, update/replace the s2 library version or disable compression for this entry.

Example fix

// before
if err := writer.Close(); err != nil {
	return nil, fmt.Errorf(certidp.ErrCannotCloseWriter, err)
}
// after
if err := writer.Close(); err != nil {
	return nil, fmt.Errorf("error closing compression writer: %w (discarding %d compressed bytes)", err, output.Len())
}
Defensive patterns

Strategy: try-catch

Try / catch

compressed, err := cache.Compress(body)
if err != nil {
	var trunc *certidp.ErrTruncatedWrite
	switch {
	case errors.As(err, &trunc):
		// discard partial output, retry once
	default:
		log.Printf("compress failed: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: LocalCache.Put -> Compress where s2.Writer.Close() returns a non-nil error, typically because an underlying write to the bytes.Buffer failed or internal s2 state is inconsistent.

Common situations: Underlying writer failure while flushing the final block; calling Close twice or using the writer after Close; panics/OOM inside the compressor surfaced at Close.

Related errors


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