apache/pulsar · error · PulsarClientException

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

Error message

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

What it means

Thrown by ProducerBuilderImpl.setMessageRoutingMode when a custom MessageRouter is configured but messageRoutingMode is not CustomPartition. The router only takes effect under CustomPartition, so any other combination (e.g. router set with RoundRobinPartition) is treated as a misconfiguration and producer creation is rejected.

Source

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

     * @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.messageRoutingMode(MessageRoutingMode.CustomPartition) when installing a custom router.
  2. Or remove the messageRouter(...) call if default routing is intended.

Example fix

// before
Producer<byte[]> p = client.newProducer().topic(topic)
    .messageRouter(new MyMessageRouter()).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.getCustomMessageRouter() != null && conf.getMessageRoutingMode() != MessageRoutingMode.CustomPartition) {
    throw new IllegalArgumentException("messageRouter set but messageRoutingMode is not CustomPartition");
}

Try / catch

try {
    producer = builder.create();
} catch (PulsarClientException e) {
    if (e.getMessage() != null && e.getMessage().contains("messageRoutingMode")) {
        builder.messageRoutingMode(MessageRoutingMode.CustomPartition); // align mode with router
        producer = builder.create();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling ProducerBuilder.messageRouter(router) while messageRoutingMode is unset, null, or explicitly set to SinglePartition/RoundRobinPartition/UseSinglePartition before create()/createAsync().

Common situations: Setting a router for partitioned topics but forgetting to switch the mode; a shared builder/helper that always installs a router; config files that supply the router but leave routing mode at default.

Related errors


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