{"record":{"id":"5276e908d832c84b","repo":"nats-io/nats-server","slug":"uncompressed-buffer-is-too-short","errorCode":null,"errorMessage":"uncompressed buffer is too short","messagePattern":"uncompressed buffer is too short","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/filestore.go","lineNumber":14501,"sourceCode":"\tc.OriginalSize = 0\n\tif len(b) < 5 { // 4 + min 1 for uvarint uint64\n\t\treturn 0, nil\n\t}\n\tif b[0] != 'c' || b[1] != 'm' || b[2] != 'p' {\n\t\treturn 0, nil\n\t}\n\tvar n int\n\tc.Algorithm = StoreCompression(b[3])\n\tc.OriginalSize, n = binary.Uvarint(b[4:])\n\tif n <= 0 {\n\t\treturn 0, fmt.Errorf(\"metadata incomplete\")\n\t}\n\treturn 4 + n, nil\n}\n\nfunc (alg StoreCompression) Compress(buf []byte) ([]byte, error) {\n\tif len(buf) < checksumSize {\n\t\treturn nil, fmt.Errorf(\"uncompressed buffer is too short\")\n\t}\n\tbodyLen := int64(len(buf) - checksumSize)\n\tvar output bytes.Buffer\n\tvar writer io.WriteCloser\n\tswitch alg {\n\tcase NoCompression:\n\t\treturn buf, nil\n\tcase S2Compression:\n\t\twriter = s2.NewWriter(&output)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"compression algorithm not known\")\n\t}\n\n\tinput := bytes.NewReader(buf[:bodyLen])\n\tchecksum := buf[bodyLen:]\n\n\t// Compress the block content, but don't compress the checksum.\n\t// We will preserve it at the end of the block as-is.","sourceCodeStart":14483,"sourceCodeEnd":14519,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/filestore.go#L14483-L14519","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure every buffer passed to Compress ends with the checksum (len >= checksumSize) before compression","Fix the block builder to append the checksum even for empty/small bodies","If compressing raw data yourself, don't route it through StoreCompression — compress only the body region","Add a pre-call length check so callers fail fast with context"],"exampleFix":"// before\nalg.Compress(buf) // panics-ish: uncompressed buffer is too short\n// after\nif len(buf) < checksumSize {\n\treturn fmt.Errorf(\"block %d bytes too short to compress\", len(buf))\n}\nalg.Compress(buf)","handlingStrategy":"validation","validationCode":"if len(buf) < checksumSize {\n\treturn fmt.Errorf(\"need at least %d bytes, got %d\", checksumSize, len(buf))\n}\nalg.Compress(buf)","typeGuard":"func compressible(buf []byte) bool { return len(buf) >= checksumSize }","tryCatchPattern":"out, err := alg.Compress(buf)\nif err != nil && strings.Contains(err.Error(), \"too short\") {\n\t// caller passed a malformed block; fix builder\n\treturn errMalformedBlock\n}","preventionTips":["Always append the checksum before compression","Centralize block building in one function","Unit-test compression with empty and minimum-size blocks","Assert len(buf) >= checksumSize in block constructors"],"tags":["jetstream","filestore","compression","validation"],"backgroundTag":"compression-buffer-too-short","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}