apache/pulsar · error · IllegalArgumentException

Invalid namespace format. expected <tenant>/<namespace> but

Error message

Invalid namespace format. expected <tenant>/<namespace> but got: ${namespace}

What it means

This is the outer wrapper thrown by the NamespaceName constructor's catch block: any IllegalArgumentException (including the V1-unsupported and bad-format messages) or NullPointerException raised while parsing is rethrown with the message 'Invalid namespace format. expected <tenant>/<namespace> but got: <input>'. It tells the developer the library expects exactly '<tenant>/<namespace>'.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/naming/NamespaceName.java:107

        // Verify it's a proper namespace
        // The namespace name is composed of <tenant>/<namespace>
        try {

            String[] parts = namespace.split("/");
            if (parts.length == 2) {
                validateNamespaceName(parts[0], parts[1]);

                tenant = parts[0];
                localName = parts[1];
            } else if (parts.length == 3) {
                throw new IllegalArgumentException(
                    "V1 namespace names (with cluster component) are no longer supported. "
                    + "Please use the V2 format: '<tenant>/<namespace>'. Got: " + namespace);
            } else {
                throw new IllegalArgumentException("Invalid namespace format. namespace: " + namespace);
            }
        } catch (IllegalArgumentException | NullPointerException e) {
            throw new IllegalArgumentException("Invalid namespace format."
                    + " expected <tenant>/<namespace>"
                    + " but got: " + namespace, e);
        }
        this.namespace = namespace;
    }

    public String getTenant() {
        return tenant;
    }

    public String getLocalName() {
        return localName;
    }

    public String getPersistentTopicName(String localTopic) {
        return getTopicName(TopicDomain.persistent, localTopic);
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Inspect the 'but got:' portion of the message and correct the input to the '<tenant>/<namespace>' form.
  2. If the input is a V1 name (three segments), remove the cluster segment: 'tenant/cluster/ns' -> 'tenant/ns'.
  3. If the input is null, fix the configuration or caller so a real namespace string is supplied.
  4. If tenant or namespace segments are empty or contain illegal characters, rename them to valid non-empty identifiers.
  5. Pre-validate with NamespaceName.validateNamespaceName(tenant, namespace) or NamespaceName.isValid(String) before calling get().

Example fix

// before
NamespaceName ns = NamespaceName.get("my-tenant/us-west/my-ns"); // V1
// after
NamespaceName ns = NamespaceName.get("my-tenant/my-ns"); // V2
Defensive patterns

Strategy: try-catch

Validate before calling

if (ns != null && ns.matches("[^/]+/[^/]+")) {
    NamespaceName n = NamespaceName.get(ns);
}

Type guard

static Optional<NamespaceName> tryParseNamespace(String ns) {
    try {
        return Optional.of(NamespaceName.get(ns));
    } catch (IllegalArgumentException e) {
        return Optional.empty();
    }
}

Try / catch

try {
    NamespaceName ns = NamespaceName.get(raw);
} catch (IllegalArgumentException e) {
    // message includes 'but got: <input>' plus cause (V1 name, bad chars, null)
    throw new ConfigurationException("Namespace must be '<tenant>/<namespace>' (V2). Cause: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: NamespaceName.get(...) with a null input (NPE at namespace.split), a V1 three-part name 'tenant/cluster/namespace' (the inner V1-not-supported IAE), an invalid tenant/localName rejected by NamedEntity.checkName, or 1/4+ segment names.

Common situations: Upgrading code that still uses V1 namespace names with a cluster component; empty tenant or namespace segments like 'tenant/' or '/default'; illegal characters in tenant or namespace names; null namespace strings from unset configuration.

Related errors


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