apache/pulsar · error · IllegalArgumentException
Invalid messageId base64 value
Error message
Invalid messageId base64 value
What it means
ReaderHandler.getMessageId decodes the 'messageId' query parameter from Base64 before reconstructing a MessageIdImpl. When the parameter is not valid Base64, java.util.Base64 throws IllegalArgumentException and the handler rethrows it with the 'Invalid messageId base64 value' message. This happens before the more specific 'Invalid messageId value' check that validates the decoded bytes.
Source
Thrown at pulsar-websocket/src/main/java/org/apache/pulsar/websocket/ReaderHandler.java:399
int size = DEFAULT_RECEIVER_QUEUE_SIZE;
if (queryParams.containsKey("receiverQueueSize")) {
size = Math.min(Integer.parseInt(queryParams.get("receiverQueueSize")), DEFAULT_RECEIVER_QUEUE_SIZE);
}
return size;
}
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
- URL-encode the messageId parameter (Base64 '+' and '/' become %2B and %2F)
- Verify the messageId string is complete and copied from the corresponding base64 encoding of the raw message id
- Use the literal 'earliest' or 'latest' when you want boundary positions instead of a concrete id
- Fix the message id at the source: read it from the received message's message-id field, not hand-crafted strings
Example fix
// before GET /websocket/reader/t/p?messageId=CgAAAA== // after GET /websocket/reader/t/p?messageId=CgAAAA%3D%3D
Defensive patterns
Strategy: validation
Validate before calling
String safe = URLEncoder.encode(messageIdParam, StandardCharsets.UTF_8); if (messageIdParam.isEmpty() || !(messageIdParam.equals("earliest") || messageIdParam.equals("latest") || safe.equals(messageIdParam))) { throw new IllegalArgumentException("messageId must be earliest, latest, or URL-encoded base64"); } Type guard
boolean isUrlSafeBase64(String s) { return s.matches("[A-Za-z0-9+/=\-%.]*") && !s.isEmpty(); } Try / catch
try { openReader(topic, messageIdParam); } catch (IllegalArgumentException e) { log.warn("bad messageId param: {}", messageIdParam); openReader(topic, "latest"); } Prevention
- URL-encode query parameters, especially base64 (+, /, =)
- Use the literal earliest/latest for boundary reads
- Copy message ids from the SDK's base64 encoder, not manual transcription
- Unit-test the reader URL builder with ids containing +/=
When it happens
Trigger: GET /websocket/reader/<topic> with ?messageId=<value> where value contains characters outside the Base64 alphabet (spaces, '+', unencoded '/' or '='), e.g. a URL-unencoded or truncated message id.
Common situations: Not URL-encoding a raw message id that contains '+/=' characters; copying a message id from logs and truncating it; sending an empty or non-id string such as 'latest' misspelled.
Related errors
- Missing required permitMessages field for 'permit' command
- Invalid messageId value
- Timeout during delete operation
- Timeout during close operation
- Timeout during open-cursor operation
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/1a9d6157539f1b75.
Report an issue: GitHub.