apache/pulsar · error · java.lang.IllegalArgumentException

Cannot compare with ${other.getClass()}

Error message

Cannot compare with ${other.getClass()}

What it means

MessageIdV5.compareTo only supports comparing against other MessageIdV5 instances. If passed any other implementation of org.apache.pulsar.client.api.v5.MessageId, it throws IllegalArgumentException('Cannot compare with <class>') because segment-ordering semantics are undefined across implementations.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/MessageIdV5.java:338

        int count = buf.getInt();
        Map<Long, MessageId> out = new HashMap<>(count);
        for (int i = 0; i < count; i++) {
            long segId = buf.getLong();
            int idLen = buf.getInt();
            if (idLen < 0 || idLen > buf.remaining()) {
                throw new IOException("Invalid MessageIdV5 data: bad inner id length");
            }
            byte[] idBytes = new byte[idLen];
            buf.get(idBytes);
            out.put(segId, MessageId.fromByteArray(idBytes));
        }
        return out;
    }

    @Override
    public int compareTo(org.apache.pulsar.client.api.v5.MessageId other) {
        if (!(other instanceof MessageIdV5 o)) {
            throw new IllegalArgumentException("Cannot compare with " + other.getClass());
        }
        int cmp = Long.compare(this.segmentId, o.segmentId);
        if (cmp != 0) {
            return cmp;
        }
        return this.v4MessageId.compareTo(o.v4MessageId);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof MessageIdV5 o)) {
            return false;
        }
        return segmentId == o.segmentId && v4MessageId.equals(o.v4MessageId);
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Ensure all ids being compared are MessageIdV5 instances produced by this client.
  2. Convert or unwrap other implementations to MessageIdV5 before comparing.
  3. In tests, build expected values via MessageIdV5 (or its factory methods) rather than mocks.
  4. If you need mixed comparison, write a comparator that handles each type explicitly instead of relying on compareTo.

Example fix

// before
ids.sort(Comparator.naturalOrder()); // list mixes id types
// after
ids.sort((a, b) -> {
    if (a instanceof MessageIdV5 a5 && b instanceof MessageIdV5 b5) return a5.compareTo(b5);
    throw new IllegalStateException("Mixed id types in collection");
});
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(other instanceof MessageIdV5)) {
    throw new IllegalArgumentException("compareTo requires MessageIdV5, got "
            + other.getClass().getName());
}

Type guard

static boolean isComparableV5(org.apache.pulsar.client.api.v5.MessageId id) {
    return id instanceof MessageIdV5;
}

Try / catch

try {
    cmp = a.compareTo(b);
} catch (IllegalArgumentException e) {
    log.error("Mixed message id types in ordered collection: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Mixing message id types in the same collection (e.g. sorting a List mixing MessageIdV5 and another v5 MessageId implementation, or a sentinel/stub id), then calling Collections.sort or compareTo.

Common situations: Collecting ids from two client APIs into one set; a test helper returning a mock v5 MessageId; a custom MessageId implementation from a fork being compared with stock MessageIdV5.

Related errors


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