nats-io/nats-server · error

short write on checksum (%d != %d)

Error message

short write on checksum (%d != %d)

What it means

Returned when output.Write(checksum) succeeds but writes fewer bytes than checksumSize, indicating a short write. A truncated checksum would make the block undetectably corrupt on read, so the library rejects it.

Source

Thrown at server/filestore.go:14533

	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])

	var reader io.ReadCloser
	switch alg {
	case NoCompression:
		return buf, nil
	case S2Compression:
		reader = io.NopCloser(s2.NewReader(input))

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. If output is a custom writer, make Write loop until the full slice is consumed or return an error
  2. Use bytes.Buffer or another writer that always writes fully for the checksum append
  3. Treat this as a bug in the output writer; replace it with a stdlib buffer

Example fix

// before (custom writer may short-write)
output = myWriter{}
// after
output = bytes.NewBuffer(make([]byte, 0, bodyLen+checksumSize))
Defensive patterns

Strategy: validation

Validate before calling

if cw, ok := output.(interface{ Write([]byte) (int, error) }); ok { if n, err := cw.Write([]byte("x")); err != nil || n != 1 { /* writer may short-write */ } }

Type guard

func writesFully(w io.Writer) bool { b := bytes.NewBuffer(nil); n, err := w.Write(make([]byte, 16)); return err == nil && n == 16 && (b == nil) }

Try / catch

_, err := alg.Compress(body)
if err != nil && strings.Contains(err.Error(), "short write on checksum") {
    // replace custom writer with bytes.Buffer and retry
}

Prevention

When it happens

Trigger: The output writer accepts the checksum slice but reports n != checksumSize during StoreCompression block compression.

Common situations: Custom io.Writer implementations that legally return short writes without error (io.Writer contract permits this) — most standard writers never do this.

Related errors


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