{"record":{"id":"1970282397428db6","repo":"dianping/cat","slug":"unsupported-message-type-s-197028","errorCode":null,"errorMessage":"Unsupported message type: %s.","messagePattern":"Unsupported message type: (.+?)\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"cat-core/src/main/java/com/dianping/cat/message/codec/PlainTextMessageCodec.java","lineNumber":413,"sourceCode":"\t\t\t\t\tif (child != null) {\n\t\t\t\t\t\tcount += encodeMessage(child, buf);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcount += encodeLine(transaction, buf, 'T', Policy.WITH_DURATION);\n\n\t\t\t\treturn count;\n\t\t\t}\n\t\t} else if (message instanceof Event) {\n\t\t\treturn encodeLine(message, buf, 'E', Policy.DEFAULT);\n\t\t} else if (message instanceof Trace) {\n\t\t\treturn encodeLine(message, buf, 'L', Policy.DEFAULT);\n\t\t} else if (message instanceof Metric) {\n\t\t\treturn encodeLine(message, buf, 'M', Policy.DEFAULT);\n\t\t} else if (message instanceof Heartbeat) {\n\t\t\treturn encodeLine(message, buf, 'H', Policy.DEFAULT);\n\t\t} else {\n\t\t\tthrow new RuntimeException(String.format(\"Unsupported message type: %s.\", message));\n\t\t}\n\t}\n\n\tprotected void setBufferWriter(BufferWriter writer) {\n\t\tm_writer = writer;\n\t\tm_bufferHelper = new BufferHelper(m_writer);\n\t}\n\n\tprotected static enum Policy {\n\t\tDEFAULT,\n\n\t\tWITHOUT_STATUS,\n\n\t\tWITH_DURATION;\n\n\t\tpublic static Policy getByMessageIdentifier(byte identifier) {\n\t\t\tswitch (identifier) {\n\t\t\tcase 't':","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/dianping/cat/blob/e815e74d4c2dd74edac831241f1253fcc7d25381/cat-core/src/main/java/com/dianping/cat/message/codec/PlainTextMessageCodec.java#L395-L431","documentation":"This RuntimeException is thrown by PlainTextMessageCodec.encodeMessage when a Message object passed to the plain-text encoder is not one of the five supported concrete types: Transaction, Event, Trace, Metric, or Heartbeat. The codec iterates instanceof checks (each mapped to a letter tag: t/T/A, E, L, M, H) and any custom or unknown Message implementation falls through to the else branch. It exists because the wire protocol only defines encodings for those five types, so a custom Message subtype cannot be serialized.","triggerScenarios":"Calling encodeMessage (directly or via a transport/channel that uses PlainTextMessageCodec) with a custom class implementing com.dianping.cat.message.Message that does not extend/implement Transaction, Event, Trace, Metric, or Heartbeat. Also triggered when a Transaction child list contains such a custom message (children are recursively encoded at line 396), or when a mock/test Message is attached to a real Transaction that gets flushed to the CAT server.","commonSituations":"Developers extend the Message interface to add domain-specific fields and then log it through Cat.log(...) or add it as a transaction child; upgrading CAT versions where a previously tolerated type is no longer handled; unit tests injecting hand-rolled Message fakes into a real codec pipeline.","solutions":["Replace the custom Message implementation with one of the built-in types (usually Event via new DefaultEvent(...) or Cat.logEvent) so the encoder recognizes it","If custom encoding is required, subclass the codec (PlainTextMessageCodec) and override encodeMessage to handle your type before delegating to super","Filter out non-standard messages before flushing: skip children whose type is not Transaction/Event/Trace/Metric/Heartbeat when building the message tree","Verify what object is actually reaching the codec by logging message.getClass() at the call site that constructs the tree"],"exampleFix":"// before\nclass MyCustomMessage implements Message { ... }\ntransaction.addChild(new MyCustomMessage());\n\n// after\nEvent event = new DefaultEvent();\nevent.setType(\"MyType\");\nevent.setName(\"my-event\");\nevent.setStatus(Message.SUCCESS);\ntransaction.addChild(event);","handlingStrategy":"type-guard","validationCode":"boolean encodable = m instanceof Transaction || m instanceof Event\n        || m instanceof Trace || m instanceof Metric || m instanceof Heartbeat;\nif (!encodable) {\n    // log and skip instead of feeding to the codec\n    logger.warn(\"Skipping unsupported message type: \" + m.getClass().getName());\n}","typeGuard":"public static boolean isSupportedMessageType(Message m) {\n    return m instanceof Transaction || m instanceof Event\n            || m instanceof Trace || m instanceof Metric || m instanceof Heartbeat;\n}","tryCatchPattern":"try {\n    codec.encodeMessage(message, buf);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Unsupported message type\")) {\n        logger.warn(\"Dropping unencodable message \" + message.getClass().getName(), e);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Never attach custom Message implementations to a Transaction; use built-in Event/Metric types","Add a compile-time constraint: factory methods that only return Transaction/Event/Trace/Metric/Heartbeat","In integration tests, run a real encode pass over sample message trees to catch unsupported types early"],"tags":["cat","java","codec","serialization","message-tree"],"backgroundTag":null,"analyzedSha":"e815e74d4c2dd74edac831241f1253fcc7d25381","analyzedAt":"2026-08-14T14:22:34.512Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}