nats-io/nats-server · error

uncompressed buffer is too short

Error message

uncompressed buffer is too short

What it means

StoreCompression.Compress() requires the input buffer to be at least checksumSize bytes long, because the format keeps the trailing checksum uncompressed and only compresses the body before it. A buffer shorter than the checksum can never be a valid block, so it refuses. From server/filestore.go.

Source

Thrown at server/filestore.go:14501

	c.OriginalSize = 0
	if len(b) < 5 { // 4 + min 1 for uvarint uint64
		return 0, nil
	}
	if b[0] != 'c' || b[1] != 'm' || b[2] != 'p' {
		return 0, nil
	}
	var n int
	c.Algorithm = StoreCompression(b[3])
	c.OriginalSize, n = binary.Uvarint(b[4:])
	if n <= 0 {
		return 0, fmt.Errorf("metadata incomplete")
	}
	return 4 + n, nil
}

func (alg StoreCompression) Compress(buf []byte) ([]byte, error) {
	if len(buf) < checksumSize {
		return nil, fmt.Errorf("uncompressed buffer is too short")
	}
	bodyLen := int64(len(buf) - checksumSize)
	var output bytes.Buffer
	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.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Ensure every buffer passed to Compress ends with the checksum (len >= checksumSize) before compression
  2. Fix the block builder to append the checksum even for empty/small bodies
  3. If compressing raw data yourself, don't route it through StoreCompression — compress only the body region
  4. Add a pre-call length check so callers fail fast with context

Example fix

// before
alg.Compress(buf) // panics-ish: uncompressed buffer is too short
// after
if len(buf) < checksumSize {
	return fmt.Errorf("block %d bytes too short to compress", len(buf))
}
alg.Compress(buf)
Defensive patterns

Strategy: validation

Validate before calling

if len(buf) < checksumSize {
	return fmt.Errorf("need at least %d bytes, got %d", checksumSize, len(buf))
}
alg.Compress(buf)

Type guard

func compressible(buf []byte) bool { return len(buf) >= checksumSize }

Try / catch

out, err := alg.Compress(buf)
if err != nil && strings.Contains(err.Error(), "too short") {
	// caller passed a malformed block; fix builder
	return errMalformedBlock
}

Prevention

When it happens

Trigger: Calling Compress (or a code path that compresses blocks) with a buffer smaller than checksumSize — e.g. an empty or nearly-empty block passed to the compression path by mistake.

Common situations: Bugs in custom tooling that calls the store's compression helpers directly; a block-building routine that produced an empty body without the checksum suffix; tests feeding synthetic buffers.

Related errors


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