apache/pulsar · error · IllegalArgumentException
Cannot derive a tenant/namespace from --regex pattern '${pat
Error message
Cannot derive a tenant/namespace from --regex pattern '${pattern}'. Use a fully-qualified pattern, e.g. persistent://tenant/namespace/.* What it means
namespaceFromPattern derives the tenant/namespace to look up topics from a --regex subscription pattern by stripping the scheme and taking the first two path segments. If the pattern does not contain at least tenant/namespace after the scheme, an IllegalArgumentException is thrown because the CLI cannot determine which namespace to list topics in.
Source
Thrown at pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/CmdConsume.java:284
java.util.function.Consumer<String> namespaceFn) {
if (isRegex) {
namespaceFn.accept(namespaceFromPattern(topic));
} else {
topicFn.accept(topic);
}
}
static String namespaceFromPattern(String pattern) {
// Strip an optional persistent:// / non-persistent:// domain prefix, then take the first
// two path segments as tenant/namespace.
String rest = pattern;
int scheme = rest.indexOf("://");
if (scheme >= 0) {
rest = rest.substring(scheme + 3);
}
String[] parts = rest.split("/");
if (parts.length < 2) {
throw new IllegalArgumentException("Cannot derive a tenant/namespace from --regex pattern '"
+ pattern + "'. Use a fully-qualified pattern, e.g. persistent://tenant/namespace/.*");
}
return parts[0] + "/" + parts[1];
}
private ConsumerEncryptionPolicy buildConsumerEncryptionPolicy() {
return buildFileDecryptionPolicy(this.encKeyValue, cryptoFailureAction);
}
@VisibleForTesting
public String getWebSocketConsumeUri(String topic) {
String serviceURLWithoutTrailingSlash = serviceURL.substring(0,
serviceURL.endsWith("/") ? serviceURL.length() - 1 : serviceURL.length());
TopicName topicName = TopicName.get(topic);
String wsTopic = String.format("%s/%s/%s/%s", topicName.getDomain(), topicName.getTenant(),
topicName.getNamespacePortion(), topicName.getLocalName());
View on GitHub (pinned to 820761864e)
Solutions
- Use a fully-qualified pattern: --regex 'persistent://my-tenant/my-ns/.*'
- Keep at least tenant/namespace in the pattern before the wildcard portion, e.g. persistent://public/default/my-topic-.*
- If you truly need cross-namespace discovery, run the CLI once per namespace pattern
Example fix
// before --regex 'orders-.*' // after --regex 'persistent://public/default/orders-.*'
Defensive patterns
Strategy: validation
Validate before calling
String rest = pattern.replaceAll("^[a-z]+://", "");
String[] parts = rest.split("/");
if (parts.length < 2 || parts[0].isEmpty() || parts[1].isEmpty())
throw new IllegalArgumentException("--regex must be fully qualified: persistent://tenant/namespace/..."); Type guard
static boolean isFullyQualifiedTopicPattern(String p) {
String rest = p.contains("://") ? p.substring(p.indexOf("://") + 3) : p;
String[] parts = rest.split("/");
return parts.length >= 2 && !parts[0].isEmpty() && !parts[1].isEmpty();
} Try / catch
try {
String ns = namespaceFromPattern(pattern);
} catch (IllegalArgumentException e) {
LOG.error("{} — defaulting pattern prefix to persistent://public/default/", e.getMessage());
pattern = "persistent://public/default/" + pattern.replaceFirst("^[a-z]+://", "");
} Prevention
- Always build --regex as persistent://tenant/namespace/<body> even when wildcarding the topic part
- Never pass bare topic wildcards like 'orders-.*' to --regex
- Add a preflight check in scripts that splits the pattern and asserts tenant/ns segments exist
- For cross-namespace discovery, loop over namespaces with one fully-qualified pattern each
When it happens
Trigger: Passing a --regex like '.*', 'mytopic-.*', or 'topic://x' — anything that after removing 'scheme://' yields fewer than 2 slash-separated segments when applyTopicSelection calls namespaceFromPattern.
Common situations: Writing a regex meant to match topics across all namespaces; shortening persistent://tenant/ns/topic.* to just topic.*; confusing --regex (needs fully-qualified pattern) with plain topic-name matching.
Related errors
- Label name '%s' is invalid: must match the regex [a-zA-Z_][a
- ${paramName} cannot be bigger than <${maxValue}>!
- ${paramName} cannot be less than or equal to <0>!
- The value of ${paramName} can't be empty
- ${name} cannot be less than <${min}>!
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/eff8de90f70ed338.
Report an issue: GitHub.