nats-io/nats-server · error
error reading compression reader: %w
Error message
error reading compression reader: %w
What it means
Returned when io.ReadAll(reader) fails while draining the decompressed block content from the s2 reader. The s2 decoding error (corrupt stream, unexpected EOF) is wrapped with %w so the original cause is preserved.
Source
Thrown at server/filestore.go:14561
bodyLen := int64(len(buf) - checksumSize)
input := bytes.NewReader(buf[:bodyLen])
var reader io.ReadCloser
switch alg {
case NoCompression:
return buf, nil
case S2Compression:
reader = io.NopCloser(s2.NewReader(input))
default:
return nil, fmt.Errorf("compression algorithm not known")
}
// Decompress the block content. The checksum isn't compressed so
// we can preserve it from the end of the block as-is.
checksum := buf[bodyLen:]
output, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("error reading compression reader: %w", err)
}
output = append(output, checksum...)
return output, reader.Close()
}
// writeFileWithOptionalSync is equivalent to os.WriteFile() but optionally
// sets O_SYNC on the open file if SyncAlways is set. The dios semaphore is
// handled automatically by this function, so don't wrap calls to it in dios.
func (fs *fileStore) writeFileWithOptionalSync(name string, data []byte, perm fs.FileMode) error {
sync := fs.syncAlways.Load() || fs.syncOnFlush.Load()
return writeAtomically(fs.dios, name, data, perm, sync)
}
func writeFileWithSync(dios *diskIOSemaphore, name string, data []byte, perm fs.FileMode) error {
return writeAtomically(dios, name, data, perm, true)
}
View on GitHub (pinned to 3a66a489d2)
Solutions
- Verify the stored block's integrity (file size vs expected bodyLen + checksumSize) and re-fetch/re-sync the block
- Check disk health (SMART) and filesystem logs for corruption
- Confirm the buffer passed to Decompress includes the full body plus trailing checksum
- Upgrade NATS Server in case of a known decompression bug; restore affected blocks from backup
Defensive patterns
Strategy: try-catch
Validate before calling
if len(buf) <= checksumSize { return errors.New("no compressed body to read") } Type guard
func isS2Corrupt(err error) bool { return err != nil && strings.Contains(err.Error(), "error reading compression reader") } Try / catch
out, err := alg.Decompress(buf)
if err != nil {
if strings.Contains(err.Error(), "error reading compression reader") {
// corrupted/truncated s2 stream: restore block from backup or resync
}
return err
} Prevention
- Enable fsync/durability so blocks are not half-written on crash
- Run filesystem checks after unclean shutdowns; watch for disk errors
- Keep backups/replicas of filestore blocks; verify sizes before decompressing
When it happens
Trigger: io.ReadAll on the s2 decompression reader errors because the compressed body bytes are corrupt or truncated (e.g. stored block damaged on disk, wrong slice boundaries passed to Decompress).
Common situations: Disk corruption or bit rot in filestore block files, a crash mid-write leaving a partial compressed body, checksum not catching damage before decompression, or decompressing a buffer sliced incorrectly (off-by-one cutting the body).
Related errors
- failed to decompress original block: %w
- compressed buffer is too short
- error writing to compression writer: %w
- invalid retained message flags
- failed to decompress block: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/8c7999ce89c56cac.
Report an issue: GitHub.