{"record":{"id":"47550c8febaa474a","repo":"dianping/cat","slug":"unrecognized-version-s-for-binary-metric-bag","errorCode":null,"errorMessage":"Unrecognized version(%s) for binary metric bag!","messagePattern":"Unrecognized version\\((.+?)\\) for binary metric bag!","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"cat-core/src/main/java/com/dianping/cat/message/codec/NativeMetricBagDecoder.java","lineNumber":42,"sourceCode":"\t\t\tbag.getMetrics().add(metric);\n\t\t}\n\n\t\treturn bag;\n\t}\n\n\tprivate void decodeHeader(Context ctx, DefaultMetricBag bag) {\n\t\tString version = ctx.readVersion();\n\n\t\tif (\"NM1\".equals(version)) {\n\t\t\tString domain = ctx.readString();\n\t\t\tString hostName = ctx.readString();\n\t\t\tString ipAddress = ctx.readString();\n\n\t\t\tbag.setDomain(domain);\n\t\t\tbag.setHostName(hostName);\n\t\t\tbag.setIpAddress(ipAddress);\n\t\t} else {\n\t\t\tthrow new RuntimeException(String.format(\"Unrecognized version(%s) for binary metric bag!\", version));\n\t\t}\n\t}\n\n\tprivate Metric decodeMetric(Context ctx) {\n\t\tlong timestamp = ctx.readLong();\n\t\tString name = ctx.readString();\n\t\tString kind = ctx.readString();\n\t\tint count = ctx.readInt();\n\t\tlong sum = ctx.readLong();\n\t\tlong duration = ctx.readLong();\n\n\t\treturn new MyMetric(timestamp, name, kind, count, Double.longBitsToDouble(sum), duration);\n\t}\n\n\tprivate static class Context {\n\t\tprivate static Charset UTF_8 = Charset.forName(\"UTF-8\");\n\n\t\tprivate ByteBuf m_buf;","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/dianping/cat/blob/e815e74d4c2dd74edac831241f1253fcc7d25381/cat-core/src/main/java/com/dianping/cat/message/codec/NativeMetricBagDecoder.java#L24-L60","documentation":"NativeMetricBagDecoder.decodeHeader reads a 3-byte version from an NM1 metric-bag payload and throws RuntimeException(\"Unrecognized version(%s) for binary metric bag!\") unless it is literally 'NM1'. This validates the metric channel's protocol before reading domain/host/ip, analogous to the tree codec's header check.","triggerScenarios":"A metric payload arrives with a version tag other than NM1 — newer client protocol, corrupted first 3 bytes, or a buffer routed to the metric decoder that was actually a PT1/NT1 message.","commonSituations":"Mixed client/server versions during upgrade; router/dispatcher bugs sending trees to the metric-bag decoder; truncated TCP payloads where the 3 header bytes are cut.","solutions":["Hex-dump the payload's first three bytes; compare with 'NM1'.","Pin cat-client and cat-core versions so the metric protocol tag matches.","Fix the dispatch that routes buffers to NativeMetricBagDecoder — only NM1-tagged buffers belong there.","Verify TCP framing delivers at least the 3 header bytes before decode is attempted."],"exampleFix":"// before: dispatcher routes by port only\nif (isMetricPort) bag = metricDecoder.decode(buf); // tree arrives -> throws\n\n// after: dispatch by tag\nString tag = buf.toString(0, 3, UTF_8);\nMessageTree t = \"NM1\".equals(tag) ? decodeMetric(buf) : codecHandler.decode(buf);","handlingStrategy":"validation","validationCode":"String tag = buf.toString(buf.readerIndex(), 3, CharsetUtil.UTF_8);\nif (!\"NM1\".equals(tag)) { buf.release(); return; }","typeGuard":null,"tryCatchPattern":"catch (RuntimeException e) { if (e.getMessage().contains(\"binary metric bag\")) { drop payload; log peer; } else throw e; }","preventionTips":["Dispatch by 3-byte tag, not by port or configuration guesses.","Keep metric-channel clients version-aligned with the server."],"tags":["protocol","metrics","versioning","dispatch"],"backgroundTag":null,"analyzedSha":"e815e74d4c2dd74edac831241f1253fcc7d25381","analyzedAt":"2026-08-14T14:22:34.512Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}