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

  1. Only acknowledge messages received from the consumer (they always carry an ID)
  2. If constructing messages for tests, set a valid MessageId (e.g., new MessageIdImpl(ledgerId, entryId, partitionIndex))
  3. 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

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


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