apache/pulsar · error · RestException
Please let the subscription only contains '/w(a-zA-Z_0-9)' o
Error message
Please let the subscription only contains '/w(a-zA-Z_0-9)' or '_', the current value is ${decodedSubName} What it means
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.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java:1985
+ "It can be 'latest', 'earliest' or (ledgerId:entryId)",
content = @Content(schema = @Schema(
allowableValues = {"latest", "earliest", "ledgerId:entryId"},
defaultValue = "latest")))
ResetCursorData resetCursorData,
@Parameter(description = "Is replicated required to perform this operation")
@QueryParam("replicated") boolean replicated
) {
try {
validateTopicName(tenant, namespace, topic);
String decodedSubName = decode(encodedSubName);
// If subscription is as "a/b". The url of HTTP API that defined as
// "{tenant}/{namespace}/{topic}/{subscription}" will be like below:
// "public/default/tp/a/b", then the broker will assume it is a topic that
// using the old rule "{tenant}/{cluster}/{namespace}/{topic}/{subscription}".
// So denied to create a subscription that contains "/".
if (pulsar().getConfig().isStrictlyVerifySubscriptionName()
&& !NamedEntity.isAllowed(decodedSubName)) {
throw new RestException(Response.Status.BAD_REQUEST, "Please let the subscription only contains"
+ " '/w(a-zA-Z_0-9)' or '_', the current value is " + decodedSubName);
}
if (!topicName.isPersistent()) {
throw new RestException(Response.Status.BAD_REQUEST, "Create subscription on non-persistent topic "
+ "can only be done through client");
}
Map<String, String> subscriptionProperties = resetCursorData == null ? null :
resetCursorData.getProperties();
MessageIdImpl messageId = resetCursorData == null ? null :
new MessageIdImpl(resetCursorData.getLedgerId(), resetCursorData.getEntryId(),
resetCursorData.getPartitionIndex());
internalCreateSubscription(asyncResponse, decode(encodedSubName), messageId, authoritative,
replicated, subscriptionProperties);
} catch (WebApplicationException wae) {
asyncResponse.resume(wae);
} catch (Exception e) {
asyncResponse.resume(new RestException(e));
}View on GitHub (pinned to 820761864e)
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
Example fix
// before admin.topics().createSubscription(topic, "my/sub", MessageId.earliest); // after admin.topics().createSubscription(topic, "my_sub", MessageId.earliest);
Defensive patterns
Strategy: validation
Validate before calling
if (!subName.matches("^[A-Za-z0-9_]+$") && strictlyVerify) throw new IllegalArgumentException("invalid subscription name: " + subName); Type guard
boolean isValidSubName(String s) { return s != null && s.matches("^[A-Za-z0-9_]+$"); } Try / catch
try { admin.topics().createSubscription(topic, sub, pos); } catch (PulsarAdminException e) { if (e.getStatusCode() == 400) { /* sanitize name */ } } Prevention
- 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
When it happens
Trigger: 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.
Common situations: Client-supplied subscription names passed through unchecked in tooling; legacy subscriptions with slash-containing names; enabling isStrictlyVerifySubscriptionName on a cluster with previously accepted names.
Related errors
- cluster data is required
- Peer cluster ${peerCluster} does not exist
- Cluster ${cluster} does not exist.
- Namespace does not exist
- ClusterIds should not be null or empty
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/82c2693e82cda65c.
Report an issue: GitHub.