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

  1. Verify the stored block's integrity (file size vs expected bodyLen + checksumSize) and re-fetch/re-sync the block
  2. Check disk health (SMART) and filesystem logs for corruption
  3. Confirm the buffer passed to Decompress includes the full body plus trailing checksum
  4. 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

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


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