{"record":{"id":"4e653d647cadc7fe","repo":"apache/rocketmq","slug":"the-specified-topic-s-contains-illegal-character","errorCode":null,"errorMessage":"The specified topic[%s] contains illegal characters, allowing only %s","messagePattern":"The specified topic\\[(.+?)\\] contains illegal characters, allowing only (.+?)","errorType":"validation","errorClass":"MQClientException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/org/apache/rocketmq/client/Validators.java","lineNumber":106,"sourceCode":"        String lmqPath = msg.getUserProperty(MessageConst.PROPERTY_INNER_MULTI_DISPATCH);\n        if (StringUtils.contains(lmqPath, File.separator)) {\n            throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL,\n                \"INNER_MULTI_DISPATCH \" + lmqPath + \" can not contains \" + File.separator + \" character\");\n        }\n    }\n\n    public static void checkTopic(String topic) throws MQClientException {\n        if (UtilAll.isBlank(topic)) {\n            throw new MQClientException(\"The specified topic is blank\", null);\n        }\n\n        if (topic.length() > TOPIC_MAX_LENGTH) {\n            throw new MQClientException(\n                String.format(\"The specified topic is longer than topic max length %d.\", TOPIC_MAX_LENGTH), null);\n        }\n\n        if (isTopicOrGroupIllegal(topic)) {\n            throw new MQClientException(String.format(\n                    \"The specified topic[%s] contains illegal characters, allowing only %s\", topic,\n                    \"^[%|a-zA-Z0-9_-]+$\"), null);\n        }\n    }\n\n    public static void isSystemTopic(String topic) throws MQClientException {\n        if (TopicValidator.isSystemTopic(topic)) {\n            throw new MQClientException(\n                    String.format(\"The topic[%s] is conflict with system topic.\", topic), null);\n        }\n    }\n\n    public static void isNotAllowedSendTopic(String topic) throws MQClientException {\n        if (TopicValidator.isNotAllowedSendTopic(topic)) {\n            throw new MQClientException(\n                    String.format(\"Sending message to topic[%s] is forbidden.\", topic), null);\n        }\n    }","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/client/src/main/java/org/apache/rocketmq/client/Validators.java#L88-L124","documentation":"Validators.checkTopic rejects topics containing characters outside ^[%|a-zA-Z0-9_-]+$ (checked via isTopicOrGroupIllegal). Dots, colons, spaces, slashes, and unicode are illegal; '%' is allowed because system topics like %RETRY% and %DLQ% use it.","triggerScenarios":"Creating/sending with a topic like 'order.created.v2' (dot), 'app:orders' (colon), or '订单' (unicode); topics derived from URLs or free-form strings.","commonSituations":"Porting Kafka-style dotted topic names; using domain event names verbatim ('order.created'); copy-paste introducing invisible whitespace.","solutions":["Normalize topic names to letters, digits, '-', '_' — replace '.' with '-' or '_'.","Add a startup regex check on configured topics: topic.matches(\"^[%|a-zA-Z0-9_-]+$\").","Trim whitespace when topics come from user or config input."],"exampleFix":"// before\nString topic = \"order.created.v2\";\n\n// after\nString topic = \"order-created-v2\";","handlingStrategy":"validation","validationCode":"static boolean legalTopicName(String t) {\n    return t != null && t.matches(\"^[%|a-zA-Z0-9_-]+$\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Normalize dotted event names to hyphens at the topic factory.","Validate all configured topics with the regex once at startup."],"tags":["rocketmq","topic","validation","naming"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}