{"record":{"id":"82c2693e82cda65c","repo":"apache/pulsar","slug":"please-let-the-subscription-only-contains-w-a-za","errorCode":null,"errorMessage":"Please let the subscription only contains '/w(a-zA-Z_0-9)' or '_', the current value is ${decodedSubName}","messagePattern":"Please let the subscription only contains '/w\\(a-zA-Z_0-9\\)' or '_', the current value is (.+?)","errorType":"http","errorClass":"RestException","httpStatus":400,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java","lineNumber":1985,"sourceCode":"                    + \"It can be 'latest', 'earliest' or (ledgerId:entryId)\",\n                    content = @Content(schema = @Schema(\n                            allowableValues = {\"latest\", \"earliest\", \"ledgerId:entryId\"},\n                            defaultValue = \"latest\")))\n                    ResetCursorData resetCursorData,\n            @Parameter(description = \"Is replicated required to perform this operation\")\n            @QueryParam(\"replicated\") boolean replicated\n    ) {\n        try {\n            validateTopicName(tenant, namespace, topic);\n            String decodedSubName = decode(encodedSubName);\n            // If subscription is as \"a/b\". The url of HTTP API that defined as\n            // \"{tenant}/{namespace}/{topic}/{subscription}\" will be like below:\n            // \"public/default/tp/a/b\", then the broker will assume it is a topic that\n            // using the old rule \"{tenant}/{cluster}/{namespace}/{topic}/{subscription}\".\n            // So denied to create a subscription that contains \"/\".\n            if (pulsar().getConfig().isStrictlyVerifySubscriptionName()\n                    && !NamedEntity.isAllowed(decodedSubName)) {\n                throw new RestException(Response.Status.BAD_REQUEST, \"Please let the subscription only contains\"\n                    + \" '/w(a-zA-Z_0-9)' or '_', the current value is \" + decodedSubName);\n            }\n            if (!topicName.isPersistent()) {\n                throw new RestException(Response.Status.BAD_REQUEST, \"Create subscription on non-persistent topic \"\n                        + \"can only be done through client\");\n            }\n            Map<String, String> subscriptionProperties = resetCursorData == null ? null :\n                    resetCursorData.getProperties();\n            MessageIdImpl messageId = resetCursorData == null ? null :\n                    new MessageIdImpl(resetCursorData.getLedgerId(), resetCursorData.getEntryId(),\n                            resetCursorData.getPartitionIndex());\n            internalCreateSubscription(asyncResponse, decode(encodedSubName), messageId, authoritative,\n                    replicated, subscriptionProperties);\n        } catch (WebApplicationException wae) {\n            asyncResponse.resume(wae);\n        } catch (Exception e) {\n            asyncResponse.resume(new RestException(e));\n        }","sourceCodeStart":1967,"sourceCodeEnd":2003,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java#L1967-L2003","documentation":"createSubscription validates the decoded subscription name with NamedEntity.isAllowed when the broker config isStrictlyVerifySubscriptionName is enabled; names containing '/' (or other disallowed characters) are rejected with HTTP 400. The '/' case is dangerous because the path would be reinterpreted as tenant/namespace/topic boundaries.","triggerScenarios":"PUT /admin/v2/persistent/{tenant}/{namespace}/{topic}/subscription/{subName} where subName contains '/' or characters outside [a-zA-Z_0-9] (e.g. 'my/sub' or 'sub-1' depending on the allowed set) with strict verification on.","commonSituations":"Client-supplied subscription names passed through unchecked in tooling; legacy subscriptions with slash-containing names; enabling isStrictlyVerifySubscriptionName on a cluster with previously accepted names.","solutions":["Use subscription names matching /^[A-Za-z0-9_]+$/ (per NamedEntity) when creating subscriptions","Disable isStrictlyVerifySubscriptionName only if you must accept legacy names (not recommended)","URL-encode is not enough — the decoded name itself must be valid"],"exampleFix":"// before\nadmin.topics().createSubscription(topic, \"my/sub\", MessageId.earliest);\n// after\nadmin.topics().createSubscription(topic, \"my_sub\", MessageId.earliest);","handlingStrategy":"validation","validationCode":"if (!subName.matches(\"^[A-Za-z0-9_]+$\") && strictlyVerify) throw new IllegalArgumentException(\"invalid subscription name: \" + subName);","typeGuard":"boolean isValidSubName(String s) { return s != null && s.matches(\"^[A-Za-z0-9_]+$\"); }","tryCatchPattern":"try { admin.topics().createSubscription(topic, sub, pos); } catch (PulsarAdminException e) { if (e.getStatusCode() == 400) { /* sanitize name */ } }","preventionTips":["Sanitize user-supplied subscription names to [a-zA-Z0-9_]","Remember URL-encoding does not relax server-side validation","Audit existing names before enabling isStrictlyVerifySubscriptionName"],"tags":["validation","subscription","rest-api"],"backgroundTag":"invalid-subscription-name","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}