{"record":{"id":"ab7d43afd7a220e7","repo":"nats-io/nats-server","slug":"short-write-on-body-d-d","errorCode":null,"errorMessage":"short write on body (%d != %d)","messagePattern":"short write on body \\((.+?) != (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/filestore.go","lineNumber":14523,"sourceCode":"\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.\n\tif n, err := io.CopyN(writer, input, bodyLen); err != nil {\n\t\treturn nil, fmt.Errorf(\"error writing to compression writer: %w\", err)\n\t} else if n != bodyLen {\n\t\treturn nil, fmt.Errorf(\"short write on body (%d != %d)\", n, bodyLen)\n\t}\n\tif err := writer.Close(); err != nil {\n\t\treturn nil, fmt.Errorf(\"error closing compression writer: %w\", err)\n\t}\n\n\t// Now add the checksum back onto the end of the block.\n\tif n, err := output.Write(checksum); err != nil {\n\t\treturn nil, fmt.Errorf(\"error writing checksum: %w\", err)\n\t} else if n != checksumSize {\n\t\treturn nil, fmt.Errorf(\"short write on checksum (%d != %d)\", n, checksumSize)\n\t}\n\n\treturn output.Bytes(), nil\n}\n\nfunc (alg StoreCompression) Decompress(buf []byte) ([]byte, error) {\n\tif len(buf) < checksumSize {\n\t\treturn nil, fmt.Errorf(\"compressed buffer is too short\")","sourceCodeStart":14505,"sourceCodeEnd":14541,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/filestore.go#L14505-L14541","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify len(buf) == bodyLen + checksumSize before calling Compress; recompute bodyLen from the actual buffer","Check that checksumSize matches between buffer construction and compression code","Rebuild the block buffer from source data instead of reusing a possibly truncated one","If this follows an earlier short-read, fail the block write rather than attempting compression"],"exampleFix":"// before\nbodyLen := int64(len(buf) - checksumSize) // buf misbuilt: checksum missing\nalg.Compress(buf) // short write on body (0 != 8)\n// after\nif int64(len(buf)) != bodyLen+checksumSize {\n\treturn fmt.Errorf(\"malformed block buffer %d != %d\", len(buf), bodyLen+checksumSize)\n}\nalg.Compress(buf)","handlingStrategy":"validation","validationCode":"func blockBufferValid(buf []byte, checksumSize int64) bool {\n\treturn int64(len(buf)) >= checksumSize\n}\nif !blockBufferValid(buf, checksumSize) {\n\treturn fmt.Errorf(\"buffer %d shorter than body+checksum\", len(buf))\n}\nalg.Compress(buf)","typeGuard":"func hasFullBody(buf []byte, bodyLen, checksumSize int64) bool {\n\treturn int64(len(buf)) == bodyLen+checksumSize\n}","tryCatchPattern":"out, err := alg.Compress(buf)\nif err != nil {\n\tif strings.Contains(err.Error(), \"short write on body\") {\n\t\t// parse n vs bodyLen from message; rebuild buffer from source data\n\t\treturn errMalformedBlock\n\t}\n\treturn err\n}","preventionTips":["Compute bodyLen from the same buffer you pass to Compress","Keep checksumSize defined in one place","Rebuild buffers from source instead of reusing partial ones","Assert buffer length invariant in block builders"],"tags":["jetstream","filestore","compression","io"],"backgroundTag":"compression-short-write","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}