apache/pulsar · error · PulsarClientException

When 'messageRoutingMode' is CustomPartition, 'messageRouter

Error message

When 'messageRoutingMode' is CustomPartition, 'messageRouter' should be set

What it means

Thrown by ProducerBuilderImpl.setMessageRoutingMode when the producer configuration sets messageRoutingMode to CustomPartition but supplies no custom MessageRouter. CustomPartition routing means partitions are dispatched by a user-provided router, so without one the mode is meaningless and the client refuses to create the producer.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/ProducerBuilderImpl.java:397

     * be created.
     * This method is limited to internal use. This method will only be used when the consumer creates the dlq producer.
     *
     * @param initialSubscriptionName Name of the initial subscription of the topic.
     * @return the producer builder implementation instance
     */
    public ProducerBuilderImpl<T> initialSubscriptionName(String initialSubscriptionName) {
        conf.setInitialSubscriptionName(initialSubscriptionName);
        return this;
    }

    private void setMessageRoutingMode() throws PulsarClientException {
        if (conf.getMessageRoutingMode() == null && conf.getCustomMessageRouter() == null) {
            messageRoutingMode(MessageRoutingMode.RoundRobinPartition);
        } else if (conf.getMessageRoutingMode() == null && conf.getCustomMessageRouter() != null) {
            messageRoutingMode(MessageRoutingMode.CustomPartition);
        } else if (conf.getMessageRoutingMode() == MessageRoutingMode.CustomPartition
                && conf.getCustomMessageRouter() == null) {
            throw new PulsarClientException("When 'messageRoutingMode' is " + MessageRoutingMode.CustomPartition
                + ", 'messageRouter' should be set");
        } else if (conf.getMessageRoutingMode() != MessageRoutingMode.CustomPartition
                && conf.getCustomMessageRouter() != null) {
            throw new PulsarClientException("When 'messageRouter' is set, 'messageRoutingMode' "
                    + "should be set as " + MessageRoutingMode.CustomPartition);
        }
    }

    @Override
    public String toString() {
        return conf != null ? conf.toString() : "";
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Call producerBuilder.messageRouter(myMessageRouter) whenever the routing mode is CustomPartition.
  2. Alternatively drop the CustomPartition setting and use the default RoundRobinPartition routing.
  3. If the mode comes from external config, validate that the router is present in the same place the mode is set.

Example fix

// before
Producer<byte[]> p = client.newProducer().topic(topic)
    .messageRoutingMode(MessageRoutingMode.CustomPartition).create();
// after
Producer<byte[]> p = client.newProducer().topic(topic)
    .messageRoutingMode(MessageRoutingMode.CustomPartition)
    .messageRouter(new MyMessageRouter()).create();
Defensive patterns

Strategy: validation

Validate before calling

if (conf.getMessageRoutingMode() == MessageRoutingMode.CustomPartition && conf.getCustomMessageRouter() == null) {
    throw new IllegalArgumentException("CustomPartition requires messageRouter(...)");
}

Try / catch

try {
    producer = builder.create();
} catch (PulsarClientException e) {
    if (e.getMessage() != null && e.getMessage().contains("messageRouter")) {
        builder.messageRoutingMode(MessageRoutingMode.RoundRobinPartition); // fall back to default routing
        producer = builder.create();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling ProducerBuilder.messageRoutingMode(MessageRoutingMode.CustomPartition) (or setting the equivalent config property) without also calling messageRouter(...) before create()/createAsync().

Common situations: Copy-pasting a config that mentions CustomPartition; building config programmatically where the router is set conditionally; migrating code that previously set a router via a different config field; YAML/properties-based config where the router object cannot be expressed.

Related errors


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