apache/pulsar · error · org.apache.pulsar.broker.admin.RestException

domain() invoked from wrong resource

Error message

domain() invoked from wrong resource

What it means

AdminResource.domain() infers the resource domain ('persistent' or 'non-persistent') from the request URI path. If the path does not start with either prefix, the resource was reached from a URL that is not a topic resource, so the broker throws RestException 500 'domain() invoked from wrong resource'. It is an internal invariant check, not a client-input error.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java:105

public abstract class AdminResource extends PulsarWebResource {

    protected NamespaceName namespaceName;
    protected TopicName topicName;

    protected BookKeeper bookKeeper() {
        return pulsar().getBookKeeperClient();
    }

    /**
     * Get the domain of the topic (whether it's persistent or non-persistent).
     */
    protected String domain() {
        if (uri.getPath().startsWith("persistent/")) {
            return "persistent";
        } else if (uri.getPath().startsWith("non-persistent/")) {
            return "non-persistent";
        } else {
            throw new RestException(Status.INTERNAL_SERVER_ERROR, "domain() invoked from wrong resource");
        }
    }

    // This is a stub method for Mockito
    @Override
    public void validateSuperUserAccess() {
        super.validateSuperUserAccess();
    }

    // This is a stub method for Mockito
    @Override
    protected void validateAdminAccessForTenant(String tenant) {
        super.validateAdminAccessForTenant(tenant);
    }

    // This is a stub method for Mockito
    @Override
    protected boolean isLeaderBroker() {

View on GitHub (pinned to 820761864e)

Solutions

  1. Ensure the request path is a full topic path: /admin/v2/{domain}/{tenant}/{namespace}/{topic} with domain = persistent or non-persistent.
  2. If implementing a custom resource, do not call validateTopicName/domain() from non-topic resources; use the appropriate namespace/tenant validators instead.
  3. Check reverse-proxy rewrite rules that may be mangling the URL path.
  4. If triggered by a Pulsar bug, file an issue with the full request path.

Example fix

// before (custom resource)
validateTopicName(property, namespace, topic); // path is /admin/v2/namespaces/..., domain() fails

// after
validateNamespaceName(property, namespace); // use the matching validator for the resource type
Defensive patterns

Strategy: try-catch

Validate before calling

String path = uri.getPath();
if (!path.startsWith("persistent/") && !path.startsWith("non-persistent/")) {
    throw new IllegalStateException("Topic validation requires a persistent/non-persistent topic path");
}

Type guard

boolean isTopicResourcePath(String path) {
    return path != null && (path.startsWith("persistent/") || path.startsWith("non-persistent/"));
}

Try / catch

try {
    validateTopicName(tenant, namespace, topic);
} catch (RestException e) {
    if (e.getResponse().getStatus() == 500 && e.getMessage().contains("domain() invoked from wrong resource")) {
        log.error("Wrong resource type for topic validation; check the request path");
    }
}

Prevention

When it happens

Trigger: Calling a topic-validation code path (e.g. an endpoint that ends up calling validateTopicName -> domain()) through an admin URL whose path does not begin with 'persistent/' or 'non-persistent/', such as invoking topic-only logic from a namespace- or tenant-scoped resource, or a mis-routed/mistyped custom endpoint.

Common situations: Bugs in custom admin plugins or forked REST resources that reuse AdminResource topic-validation on non-topic paths; proxy/rewrite rules that strip the 'persistent/'/'non-persistent/' prefix from the URI; calling validateTopicName outside a proper topic request context (e.g. in tests with a fake URI).

Related errors


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