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
- Call producerBuilder.messageRoutingMode(MessageRoutingMode.CustomPartition) when installing a custom router.
- 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
- Whenever you call messageRouter(...), immediately set messageRoutingMode(CustomPartition) on the same builder.
- Avoid builders that install a router unconditionally; make router installation opt-in.
- Document the mode+router coupling in shared producer-factory code.
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
- When 'messageRoutingMode' is CustomPartition, 'messageRouter
- Invalid client configuration
- Both externalExecutorProvider and internalExecutorProvider m
- ${cause.getMessage()}
- ${cause}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/dc2e2c9d0226eca7.
Report an issue: GitHub.