nats-io/nats-server · error

compressed buffer is too short

Error message

compressed buffer is too short

What it means

StoreCompression.Decompress requires the trailing checksum to be present: a buffer shorter than checksumSize cannot even contain the checksum, so it cannot be a valid compressed block. Returned before any decompression is attempted.

Source

Thrown at server/filestore.go:14541

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

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Verify the source of the buffer: check the block file was fully written (length + checksum)
  2. Don't strip the checksum from the buffer before calling Decompress
  3. Check the filestore for truncated blocks and re-fetch/re-sync the affected block
  4. Log the buffer length to confirm it is below checksumSize and trace where truncation happens

Example fix

// before
out, err := alg.Decompress(buf[:bodyOnly])
// after
if len(buf) < checksumSize { return ErrCorruptBlock }
out, err := alg.Decompress(buf)
Defensive patterns

Strategy: validation

Validate before calling

if len(buf) < checksumSize { return fmt.Errorf("cannot decompress: buffer %d < checksum size", len(buf)) }

Type guard

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

Try / catch

out, err := alg.Decompress(buf)
if err != nil {
    if strings.Contains(err.Error(), "compressed buffer is too short") {
        // block source truncated; refetch or resync the block
    }
    return err
}

Prevention

When it happens

Trigger: Calling Decompress (or any filestore read path that decompresses a stored block) with a byte slice whose len < checksumSize — e.g. an empty slice, a truncated read, or a corrupted/zero-length stored block.

Common situations: Truncated filestore block files after a crash, reading past a partially written block, a bug slicing off the checksum before calling Decompress, or feeding Decompress arbitrary bytes it was never meant to receive.

Related errors


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