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

  1. Use subscription names matching /^[A-Za-z0-9_]+$/ (per NamedEntity) when creating subscriptions
  2. Disable isStrictlyVerifySubscriptionName only if you must accept legacy names (not recommended)
  3. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/82c2693e82cda65c. Report an issue: GitHub.