apache/pulsar · error · java.lang.IllegalStateException

MessageIdV5 missing multi-topic vector — was the message del

Error message

MessageIdV5 missing multi-topic vector — was the message delivered through a multi-topic stream consumer?

What it means

Thrown by MultiTopicStreamConsumer.fanOutCumulativeAck when the MessageIdV5 lacks its multiTopicVector(). Without the per-topic id map, the consumer cannot route the cumulative ack across the topic set, so it fails with IllegalStateException.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/MultiTopicStreamConsumer.java:334

    public void acknowledgeCumulative(MessageId messageId, Transaction txn) {
        // Transactions on multi-topic are best-effort across per-topic consumers — each
        // per-topic ack is independently transactional. See note in the design doc.
        fanOutCumulativeAck(messageId, (sc, vector) -> sc.ackUpToVector(vector));
    }

    /**
     * For a cumulative ack on a multi-topic message, look up its multi-topic vector
     * and invoke the per-topic ack on every parent topic.
     */
    private void fanOutCumulativeAck(MessageId messageId,
                                     java.util.function.BiConsumer<ScalableStreamConsumer<T>,
                                             Map<Long, org.apache.pulsar.client.api.MessageId>> action) {
        if (!(messageId instanceof MessageIdV5 id)) {
            throw new IllegalArgumentException("Expected MessageIdV5, got: " + messageId.getClass());
        }
        Map<String, Map<Long, org.apache.pulsar.client.api.MessageId>> vector = id.multiTopicVector();
        if (vector == null) {
            throw new IllegalStateException("MessageIdV5 missing multi-topic vector — was the"
                    + " message delivered through a multi-topic stream consumer?");
        }
        for (var entry : vector.entrySet()) {
            PerTopic<T> state = perTopic.get(entry.getKey());
            if (state == null) {
                // Topic left the matching set since this message was enqueued: we've
                // detached it and no longer ack removed topics, so skip its slice.
                continue;
            }
            action.accept(state.consumer, entry.getValue());
        }
    }

    @Override
    public AsyncStreamConsumer<T> async() {
        return asyncView;
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Ack with ids delivered by this exact consumer instance
  2. Verify id.multiTopicVector() != null before calling acknowledgeCumulative
  3. When persisting ids, serialize the full multi-topic metadata

Example fix

// before
streamConsumer.acknowledgeCumulative(id); // vector-less id
// after
if (id instanceof MessageIdV5 v5 && v5.multiTopicVector() != null) {
    streamConsumer.acknowledgeCumulative(v5);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (id instanceof MessageIdV5 v5 && v5.multiTopicVector() == null) { handleVectorless(); }

Type guard

static boolean hasMultiTopicVector(MessageId id) { return id instanceof MessageIdV5 v5 && v5.multiTopicVector() != null; }

Try / catch

try { consumer.acknowledgeCumulative(id); } catch (IllegalStateException e) { log.warn("missing multi-topic vector", e); }

Prevention

When it happens

Trigger: Calling acknowledgeCumulative with a MessageIdV5 not produced by multi-topic stream delivery — single-topic v5 id, deserialized id missing vector metadata, or hand-constructed id.

Common situations: Persisting and later replaying ids without the multi-topic vector; acking across consumer instances; client version migration dropping metadata.

Related errors


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