apache/pulsar · error · IllegalArgumentException

Invalid messageId value

Error message

Invalid messageId value

What it means

ReaderHandler.getMessageId successfully Base64-decodes the 'messageId' parameter, but MessageIdImpl.fromByteArray cannot parse the decoded bytes into a valid MessageId (wrong length, malformed ledger/entry layout), so it rethrows as 'Invalid messageId value'. The bytes were encoded correctly but are not a Pulsar MessageId serialized form.

Source

Thrown at pulsar-websocket/src/main/java/org/apache/pulsar/websocket/ReaderHandler.java:405

    private MessageId getMessageId() {
        MessageId messageId = MessageId.latest;
        String messageIdParam = queryParams.get("messageId");
        if (isNotBlank(messageIdParam)) {
            if (messageIdParam.equals("earliest")) {
                messageId = MessageId.earliest;
            } else if (!messageIdParam.equals("latest")) {
                final byte[] decoded;
                try {
                    decoded = Base64.getDecoder().decode(messageIdParam);
                } catch (IllegalArgumentException e) {
                    throw new IllegalArgumentException("Invalid messageId base64 value", e);
                }

                try {
                    messageId = MessageIdImpl.fromByteArray(decoded);
                } catch (IOException | RuntimeException e) {
                    throw new IllegalArgumentException("Invalid messageId value", e);
                }
            }
        }
        return messageId;
    }

}

View on GitHub (pinned to 820761864e)

Solutions

  1. Serialize the id with MessageId.toByteArray() on the producer side and base64-encode those exact bytes before passing them as ?messageId
  2. Use the SDK's built-in base64 message-id helpers instead of hand-rolling the encoding
  3. Confirm the id comes from the same Pulsar cluster/version (serialization format compatibility)
  4. Fall back to 'earliest' or 'latest' when a concrete id is unavailable

Example fix

// before
String param = Base64.getEncoder().encodeToString(msgId.toString().getBytes());
// after
String param = Base64.getEncoder().encodeToString(msgId.toByteArray());
Defensive patterns

Strategy: validation

Validate before calling

byte[] raw = Base64.getDecoder().decode(messageIdParam); if (raw.length < 12) { throw new IllegalArgumentException("decoded messageId too short to be a Pulsar MessageId"); }

Type guard

boolean isPlausibleMessageId(byte[] decoded) { return decoded != null && decoded.length >= new MessageIdImpl(0,0,0).toByteArray().length; }

Try / catch

try { byte[] decoded = Base64.getDecoder().decode(param); return MessageIdImpl.fromByteArray(decoded); } catch (IOException | RuntimeException e) { return MessageId.latest; }

Prevention

When it happens

Trigger: ?messageId=<base64> decodes cleanly but the bytes are not a serialized MessageId — e.g. random data, a Pulsar broker messageId from a different serialization format, or a base64 of a string like 'ledger:entry'.

Common situations: Base64-encoding a textual message-id representation instead of the raw byte array from MessageId.toByteArray(); mixing message id formats across Pulsar versions; decoding an ack-set-augmented id whose byte layout was hand-trimmed incorrectly.

Related errors


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