apache/pulsar · error · IllegalArgumentException

DeadLetter topic %s is invalid

Error message

DeadLetter topic %s is invalid

What it means

The dead-letter topic (used to store messages that exhaust max retries) is validated with TopicName.isValid in doCommonChecks; an invalid name causes function creation to fail with this IllegalArgumentException. A DLQ must be a real, addressable Pulsar topic for the retry policy to publish to it.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:832

        }

        if (!isEmpty(functionConfig.getOutput())) {
            if (!TopicName.isValid(functionConfig.getOutput())) {
                throw new IllegalArgumentException(
                        String.format("Output topic %s is invalid", functionConfig.getOutput()));
            }
        }

        if (!isEmpty(functionConfig.getLogTopic())) {
            if (!TopicName.isValid(functionConfig.getLogTopic())) {
                throw new IllegalArgumentException(
                        String.format("LogTopic topic %s is invalid", functionConfig.getLogTopic()));
            }
        }

        if (!isEmpty(functionConfig.getDeadLetterTopic())) {
            if (!TopicName.isValid(functionConfig.getDeadLetterTopic())) {
                throw new IllegalArgumentException(
                        String.format("DeadLetter topic %s is invalid", functionConfig.getDeadLetterTopic()));
            }
        }

        if (functionConfig.getParallelism() != null && functionConfig.getParallelism() <= 0) {
            throw new IllegalArgumentException("Function parallelism must be a positive number");
        }
        // Ensure that topics aren't being used as both input and output
        verifyNoTopicClash(allInputTopics, functionConfig.getOutput());

        WindowConfig windowConfig = functionConfig.getWindowConfig();
        if (windowConfig != null) {
            // set auto ack to false since windowing framework is responsible
            // for acking and not the function framework
            @SuppressWarnings("deprecation")
            Boolean windowAutoAck = functionConfig.getAutoAck();
            if (windowAutoAck != null && windowAutoAck) {
                throw new IllegalArgumentException("Cannot enable auto ack when using windowing functionality");

View on GitHub (pinned to 820761864e)

Solutions

  1. Set a fully qualified DLQ name: persistent://public/default/my-dlq
  2. Also set maxMessageRetries >= 0 (a DLQ alone with infinite retries is rejected separately — see error 1419)
  3. Remove the deadLetterTopic if retries handling is not desired
  4. Validate with TopicName.isValid before submission

Example fix

// before
config.setDeadLetterTopic("dlq");
// after
config.setDeadLetterTopic("persistent://public/default/dlq");
config.setMaxMessageRetries(3);
Defensive patterns

Strategy: validation

Validate before calling

String dlq = config.getDeadLetterTopic();
if (dlq != null && !dlq.isEmpty() && !TopicName.isValid(dlq.trim())) {
    throw new IllegalArgumentException("Invalid DLQ topic: " + dlq);
}
if (dlq != null && !dlq.isEmpty()
        && (config.getMaxMessageRetries() == null || config.getMaxMessageRetries() < 0)) {
    throw new IllegalArgumentException("DLQ requires maxMessageRetries >= 0");
}

Type guard

boolean hasValidDlq(FunctionConfig c) {
    String t = c.getDeadLetterTopic();
    return t == null || t.isEmpty()
        || (TopicName.isValid(t.trim()) && c.getMaxMessageRetries() != null && c.getMaxMessageRetries() >= 0);
}

Try / catch

try {
    admin.functions().createFunction(functionConfig, sourceConfigLocation);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("DeadLetter")) {
        // fix deadLetterTopic format and ensure maxMessageRetries is set
    }
    throw e;
}

Prevention

When it happens

Trigger: createFunction/updateFunction where functionConfig.getDeadLetterTopic() is non-empty and fails TopicName.isValid: bare topic name, malformed persistent:// URI, trailing whitespace, or empty segments.

Common situations: Setting --dead-letter-topic on the CLI with a short name, YAML placeholders not rendered, renaming tenant/namespace elsewhere and forgetting the DLQ field.

Related errors


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