nats-io/nats-server · error
short write on body (%d != %d)
Error message
short write on body (%d != %d)
What it means
After io.CopyN, Compress() verifies that exactly bodyLen bytes were copied into the compression writer. If n != bodyLen, the body was only partially compressed (copy ended early without error), producing an invalid block, so it fails with "short write on body (%d != %d)". From server/filestore.go.
Source
Thrown at server/filestore.go:14523
var writer io.WriteCloser
switch alg {
case NoCompression:
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")View on GitHub (pinned to 3a66a489d2)
Solutions
- Verify len(buf) == bodyLen + checksumSize before calling Compress; recompute bodyLen from the actual buffer
- Check that checksumSize matches between buffer construction and compression code
- Rebuild the block buffer from source data instead of reusing a possibly truncated one
- If this follows an earlier short-read, fail the block write rather than attempting compression
Example fix
// before
bodyLen := int64(len(buf) - checksumSize) // buf misbuilt: checksum missing
alg.Compress(buf) // short write on body (0 != 8)
// after
if int64(len(buf)) != bodyLen+checksumSize {
return fmt.Errorf("malformed block buffer %d != %d", len(buf), bodyLen+checksumSize)
}
alg.Compress(buf) Defensive patterns
Strategy: validation
Validate before calling
func blockBufferValid(buf []byte, checksumSize int64) bool {
return int64(len(buf)) >= checksumSize
}
if !blockBufferValid(buf, checksumSize) {
return fmt.Errorf("buffer %d shorter than body+checksum", len(buf))
}
alg.Compress(buf) Type guard
func hasFullBody(buf []byte, bodyLen, checksumSize int64) bool {
return int64(len(buf)) == bodyLen+checksumSize
} Try / catch
out, err := alg.Compress(buf)
if err != nil {
if strings.Contains(err.Error(), "short write on body") {
// parse n vs bodyLen from message; rebuild buffer from source data
return errMalformedBlock
}
return err
} Prevention
- Compute bodyLen from the same buffer you pass to Compress
- Keep checksumSize defined in one place
- Rebuild buffers from source instead of reusing partial ones
- Assert buffer length invariant in block builders
When it happens
Trigger: io.CopyN returns n < bodyLen with nil error while compressing — the input buffer's body region is shorter than bodyLen computed from len(buf)-checksumSize, i.e. the buffer/bodyLen accounting is wrong.
Common situations: Calling Compress with a buffer where the checksum region doesn't actually occupy the tail bytes (misbuilt buffer); off-by-one in checksumSize; mixing buffers built with a different checksum size.
Related errors
- error writing to compression writer: %w
- failed to read original block from disk: %w
- failed to read existing metadata header: %w
- failed to decompress original block: %w
- failed to compress block: %w
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/ab7d43afd7a220e7.
Report an issue: GitHub.