apache/pulsar · error · PulsarClientException.InvalidMessageException
Cannot handle message with null messageId
Error message
Cannot handle message with null messageId
What it means
validateMessageId(Message) also verifies that the message carries a non-null MessageId; a message without an ID cannot be acknowledged or negatively acknowledged because the broker identifies messages by MessageId. Throws InvalidMessageException 'Cannot handle message with null messageId'. In practice this indicates a synthetic or deserialized Message whose ID was never populated.
Source
Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConsumerBase.java:428
opBatchReceive.future.completeExceptionally(
new PulsarClientException.AlreadyClosedException(
String.format("The consumer which subscribes the topic %s with subscription name %s was"
+ " already closed when cleaning and closing the consumers",
topic, subscription)));
}
}
}
protected abstract Messages<T> internalBatchReceive() throws PulsarClientException;
protected abstract CompletableFuture<Messages<T>> internalBatchReceiveAsync();
private static void validateMessageId(Message<?> message) throws PulsarClientException {
if (message == null) {
throw new PulsarClientException.InvalidMessageException("Non-null message is required");
}
if (message.getMessageId() == null) {
throw new PulsarClientException.InvalidMessageException("Cannot handle message with null messageId");
}
}
private static void validateMessageId(MessageId messageId) throws PulsarClientException {
if (messageId == null) {
throw new PulsarClientException.InvalidMessageException("Cannot handle message with null messageId");
}
}
private static void validateMessageIds(List<MessageId> messageIdList) throws PulsarClientException {
if (messageIdList == null) {
throw new PulsarClientException.InvalidMessageException("Cannot handle messages with null messageIdList");
}
for (MessageId messageId : messageIdList) {
validateMessageId(messageId);
}
}
View on GitHub (pinned to 820761864e)
Solutions
- Only acknowledge messages received from the consumer (they always carry an ID)
- If constructing messages for tests, set a valid MessageId (e.g., new MessageIdImpl(ledgerId, entryId, partitionIndex))
- Null-check message.getMessageId() before ack in defensive code paths
Example fix
// before Message<String> fake = new MessageImpl<>(); consumer.acknowledge(fake); // messageId == null // after MessageId mid = new MessageIdImpl(ledgerId, entryId, partition); consumer.acknowledge(mid); // ack by MessageId directly
Defensive patterns
Strategy: validation
Validate before calling
if (message == null || message.getMessageId() == null) {
throw new IllegalArgumentException("Message or messageId is null; cannot ack");
} Type guard
boolean hasMessageId(Message<?> m) {
return m != null && m.getMessageId() != null;
} Try / catch
try {
consumer.acknowledge(message);
} catch (PulsarClientException.InvalidMessageException e) {
log.warn("Message has no messageId; skipping ack");
} Prevention
- Never construct synthetic Message objects for ack — ack by MessageId instead
- In tests, use real messages from consumer.receive() or set MessageIdImpl explicitly
- Validate messageId before wrapping/rebuilding messages in interceptors
When it happens
Trigger: Calling acknowledge/acknowledgeAsync/acknowledgeCumulative/reconsumeLater*/validateMessages with a Message whose getMessageId() returns null — e.g., a hand-constructed MessageImpl, a mock in tests, or a message deserialized without its ID field.
Common situations: Unit-test mocks returning messages without IDs; wrapping/re-creating Message objects before ack; custom interceptors that strip or rebuild messages.
Related errors
- Non-null message is required
- Cannot handle messages with null messageIdList
- Cannot handle messages with null messages
- isDuplicated cannot accept
- The topic has a max partition index of %d, the number of par
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/174b5e1bf49fbf53.
Report an issue: GitHub.