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

  1. Inspect the wrapped cause with errors.Is/As on the returned error to find the real writer failure
  2. Verify the destination writer (output buffer/file/stream) has space and is healthy
  3. Retry the block compression once; transient I/O errors may clear
  4. 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

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


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