{"record":{"id":"d37305f6dc487b40","repo":"dianping/cat","slug":"malformed-variable-int-s","errorCode":null,"errorMessage":"Malformed variable int %s!","messagePattern":"Malformed variable int (.+?)!","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"cat-core/src/main/java/com/dianping/cat/message/codec/NativeMessageCodec.java","lineNumber":463,"sourceCode":"\n\t\tpublic long readTimestamp(ByteBuf buf) {\n\t\t\treturn readVarint(buf, 64);\n\t\t}\n\n\t\tprotected long readVarint(ByteBuf buf, int length) {\n\t\t\tint shift = 0;\n\t\t\tlong result = 0;\n\n\t\t\twhile (shift < length) {\n\t\t\t\tfinal byte b = buf.readByte();\n\t\t\t\tresult |= (long) (b & 0x7F) << shift;\n\t\t\t\tif ((b & 0x80) == 0) {\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t\tshift += 7;\n\t\t\t}\n\n\t\t\tthrow new RuntimeException(\"Malformed variable int \" + length + \"!\");\n\t\t}\n\n\t\tpublic void writeDuration(ByteBuf buf, long duration) {\n\t\t\twriteVarint(buf, duration);\n\t\t}\n\n\t\tpublic void writeId(ByteBuf buf, char id) {\n\t\t\tbuf.writeByte(id);\n\t\t}\n\n\t\tpublic void writeString(ByteBuf buf, String str) {\n\t\t\tif (str == null || str.length() == 0) {\n\t\t\t\twriteVarint(buf, 0);\n\t\t\t} else {\n\t\t\t\tbyte[] data = str.getBytes(UTF8);\n\n\t\t\t\twriteVarint(buf, data.length);\n\t\t\t\tbuf.writeBytes(data);","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/dianping/cat/blob/e815e74d4c2dd74edac831241f1253fcc7d25381/cat-core/src/main/java/com/dianping/cat/message/codec/NativeMessageCodec.java#L445-L481","documentation":"Context.readVarint in NativeMessageCodec decodes LEB128-style variable-length ints: each byte contributes 7 bits and must terminate with a high-bit-clear byte within 'length' shifts. If the loop exhausts the shift budget, the stream is malformed and RuntimeException(\"Malformed variable int <length>!\") is thrown. Note the message interpolates the maximum shift count, not the actual bytes read.","triggerScenarios":"Decoding an NT1 message where a varint field (durations, string lengths) is unterminated — every byte has bit 7 set past the allowed width. Caused by truncated payloads, buffer corruption, or desynchronized decoding (reading a varint where a different field type sits).","commonSituations":"TCP fragmentation cutting a message mid-varint when framing is wrong; server/client version drift changing field order so a string length is read as a varint body; extremely large durations encoded with a different varint width than the reader allows.","solutions":["Log the reader index where the failure occurs and hex-dump surrounding bytes to see whether the varint is truncated or misaligned.","Verify the framing layer delivers complete messages (LengthFieldBasedFrameDecoder with correct lengthAdjustment).","Ensure encoder and decoder share the same field order and varint encoding (same codec version both ends).","Check that durations/counts being encoded fit the varint width the reader supports (<= 9 bytes for 64-bit)."],"exampleFix":"// before: delimiter-based framing can split mid-message\nnew DelimiterBasedFrameDecoder(...); // varint cut at frame edge -> throws\n\n// after: length-prefixed framing\nnew LengthFieldBasedFrameDecoder(1024*1024, 0, 4, 0, 4);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (RuntimeException e) { if (e.getMessage().startsWith(\"Malformed variable int\")) { log readerIndex + hexdump window; drop connection; } else throw e; }","preventionTips":["Length-prefix every binary message so varints never straddle frames.","Fuzz-test codecs with truncated buffers to confirm they fail closed.","Pin encoder/decoder versions."],"tags":["protocol","binary-decoding","varint","corruption"],"backgroundTag":null,"analyzedSha":"e815e74d4c2dd74edac831241f1253fcc7d25381","analyzedAt":"2026-08-14T14:22:34.512Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}