apache/pulsar · error · PulsarClientException.InvalidMessageException

Cannot handle messages with null messageIdList

Error message

Cannot handle messages with null messageIdList

What it means

validateMessageIds(List<MessageId>) first rejects a null list with InvalidMessageException 'Cannot handle messages with null messageIdList', then validates each element. Multi-message acknowledgment (acknowledge(List<MessageId>) and transactional acks via doAcknowledgeWithTxn) requires a concrete, non-empty-capable list because the broker needs the set of IDs to ack.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConsumerBase.java:440

    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);
        }
    }

    private static void validateMessages(Messages<?> messages) throws PulsarClientException {
        if (messages == null) {
            throw new PulsarClientException.InvalidMessageException("Cannot handle messages with null messages");
        }
        for (Message<?> message : messages) {
            validateMessageId(message);
        }
    }
    @Override
    public void acknowledge(Message<?> message) throws PulsarClientException {
        validateMessageId(message);
        acknowledge(message.getMessageId());

View on GitHub (pinned to 820761864e)

Solutions

  1. Initialize ID collections as empty lists (Collections.emptyList()) instead of null
  2. Null-check the list before calling acknowledge(List<MessageId>)
  3. Guard the batch-ack behind a isEmpty()/!= null check

Example fix

// before
List<MessageId> ids = aggregator.getIds(); // may be null
consumer.acknowledge(ids);
// after
List<MessageId> ids = aggregator.getIds();
if (ids != null && !ids.isEmpty()) {
    consumer.acknowledge(ids);
}
Defensive patterns

Strategy: validation

Validate before calling

if (idList == null || idList.isEmpty()) {
    return; // nothing to ack
}

Type guard

boolean isAckableList(List<MessageId> ids) {
    return ids != null && !ids.isEmpty() && ids.stream().allMatch(java.util.Objects::nonNull);
}

Try / catch

try {
    consumer.acknowledge(messageIdList);
} catch (PulsarClientException.InvalidMessageException e) {
    log.warn("Invalid messageIdList: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Calling consumer.acknowledge((List<MessageId>) null) or acknowledgeAsync with a null list — usually when the list was built conditionally and never initialized, or a method parameter was forwarded without checking.

Common situations: Aggregating IDs for batch acking where the aggregation step returned null instead of an empty list; refactoring from single-ack to batch-ack while keeping old nullable variables.

Related errors


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