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

  1. Verify len(buf) == bodyLen + checksumSize before calling Compress; recompute bodyLen from the actual buffer
  2. Check that checksumSize matches between buffer construction and compression code
  3. Rebuild the block buffer from source data instead of reusing a possibly truncated one
  4. 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

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


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