nats-io/nats-server · error
error closing compression writer: %w
Error message
error closing compression writer: %w
What it means
This error is returned by StoreCompression's compress path (server/filestore.go) when writer.Close() fails after the compressed body was fully written. Closing a compression writer (e.g. s2.Writer) flushes internal buffers and writes the stream trailer; if that flush fails the block would be corrupt, so the library refuses to return it. The underlying writer error is wrapped with %w so errors.Is/As still work.
Source
Thrown at server/filestore.go:14526
return buf, nil
case S2Compression:
writer = s2.NewWriter(&output)
default:
return nil, fmt.Errorf("compression algorithm not known")
}
input := bytes.NewReader(buf[:bodyLen])
checksum := buf[bodyLen:]
// Compress the block content, but don't compress the checksum.
// We will preserve it at the end of the block as-is.
if n, err := io.CopyN(writer, input, bodyLen); err != nil {
return nil, fmt.Errorf("error writing to compression writer: %w", err)
} else if n != bodyLen {
return nil, fmt.Errorf("short write on body (%d != %d)", n, bodyLen)
}
if err := writer.Close(); err != nil {
return nil, fmt.Errorf("error closing compression writer: %w", err)
}
// Now add the checksum back onto the end of the block.
if n, err := output.Write(checksum); err != nil {
return nil, fmt.Errorf("error writing checksum: %w", err)
} else if n != checksumSize {
return nil, fmt.Errorf("short write on checksum (%d != %d)", n, checksumSize)
}
return output.Bytes(), nil
}
func (alg StoreCompression) Decompress(buf []byte) ([]byte, error) {
if len(buf) < checksumSize {
return nil, fmt.Errorf("compressed buffer is too short")
}
bodyLen := int64(len(buf) - checksumSize)
input := bytes.NewReader(buf[:bodyLen])View on GitHub (pinned to 3a66a489d2)
Solutions
- Inspect the wrapped cause with errors.Is/As on the returned error to find the real writer failure
- Verify the destination writer (output buffer/file/stream) has space and is healthy
- Retry the block compression once; transient I/O errors may clear
- If using a custom writer, ensure Close only errors on genuine flush failures
Example fix
// before
if err := writer.Close(); err != nil {
return nil, fmt.Errorf("error closing compression writer: %w", err)
}
// after
if err := writer.Close(); err != nil {
if errors.Is(err, os.ErrNoSpace) {
return nil, fmt.Errorf("compression close failed, disk full: %w", err)
}
return nil, fmt.Errorf("error closing compression writer: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if outWriter == nil || unreliable { return errors.New("need healthy output writer before compressing") } Type guard
func isCompressionCloseError(err error) bool { return err != nil && strings.Contains(err.Error(), "error closing compression writer") } Try / catch
data, err := alg.Compress(block)
if err != nil {
var ce *fsErr
if errors.As(err, &ce) && strings.Contains(err.Error(), "closing compression writer") {
// retry or surface storage I/O problem
}
return err
} Prevention
- Compress to bytes.Buffer-backed writers which rarely fail on Close
- Monitor disk space where the filestore writes
- Treat any wrapped Close error as a storage-layer alarm, not a data problem
When it happens
Trigger: io.Writer to Close (the compression writer over an output buffer or stream) returns a non-nil error when closed after a successful io.CopyN of bodyLen bytes during block compression in the filestore.
Common situations: Disk-full or I/O errors when output is a file-backed writer, corruption of an in-memory buffer implementation, or a custom io.WriteCloser whose Close does an extra flush that fails.
Related errors
- error writing to compression writer: %w
- short write on body (%d != %d)
- Error creating msg block file: %v
- failed to load block from disk: %w
- failed to decompress block: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/cecd712e442b8154.
Report an issue: GitHub.