apache/pulsar · error · IOException
Missing required permitMessages field for 'permit' command
Error message
Missing required permitMessages field for 'permit' command
What it means
ConsumerHandler rejects a WebSocket 'permit' command that omits the required permitMessages field. The proxy uses permitMessages to resume (or further throttle) message delivery in pull mode; without a numeric value it cannot update the pending-credit counter, so it fails the frame with an IOException, which closes the consumer session.
Source
Thrown at pulsar-websocket/src/main/java/org/apache/pulsar/websocket/ConsumerHandler.java:382
MessageId originalMsgId = messageIdCache.asMap().remove(command.messageId);
if (originalMsgId != null) {
consumer.negativeAcknowledge(originalMsgId);
} else {
consumer.negativeAcknowledge(msgId);
}
checkResumeReceive();
}
private void handlePermit(ConsumerCommand command) throws IOException {
log.debug()
.attr("topic", consumer.getTopic())
.attr("subscription", subscription)
.attr("received", command.permitMessages)
.attr("request", getSession().getRemoteSocketAddress())
.log("/ ] Received permits request from");
if (command.permitMessages == null) {
throw new IOException("Missing required permitMessages field for 'permit' command");
}
if (this.pullMode) {
int pending = pendingMessages.getAndAdd(-command.permitMessages);
if (pending >= 0) {
// Resume delivery
receiveMessage();
}
}
}
@Override
public void close() throws IOException {
if (consumer != null) {
if (!this.service.removeConsumer(this)) {
log.warn().attr("topic", consumer.getTopic()).log("Failed to remove consumer handler");
}
consumer.closeAsync().thenAccept(x -> {
log.debug().attr("topic", consumer.getTopic()).log("Closed consumer asynchronously");View on GitHub (pinned to 820761864e)
Solutions
- Always include an integer permitMessages field in every 'permit' command frame
- Update the client SDK/library so permit frames are serialized with permitMessages set
- Validate the outbound JSON on the client before sending (assert permitMessages is a non-negative number)
- If stopping consumption entirely, close the consumer instead of sending a zero/null permit
Example fix
// before
{"type":"permit"}
// after
{"type":"permit","permitMessages":10} Defensive patterns
Strategy: validation
Validate before calling
if (cmd == null || cmd.getPermitMessages() == null || cmd.getPermitMessages() < 0) { throw new IllegalArgumentException("permit command requires non-negative permitMessages"); } Type guard
boolean hasPermitMessages(CommandPermit cmd) { return cmd != null && cmd.getPermitMessages() != null && cmd.getPermitMessages() >= 0; } Try / catch
try { handler.handlePermit(cmd); } catch (IOException e) { log.error("permit frame rejected: {}", e.getMessage()); closeSession(); } Prevention
- Always serialize permitMessages in permit frames
- Add client-side schema validation for outbound command JSON
- Use an official Pulsar client SDK rather than hand-rolled WebSocket frames
- Integration-test permit flow (pause/resume) in CI
When it happens
Trigger: A WebSocket client sends a JSON frame with command type 'permit' but no permitMessages property (or sends null). handlePermit parses the command and throws before touching pendingMessages.
Common situations: Hand-rolled client code emitting permit frames; client SDK version mismatch where the producer of the frame serializes a null permit; a proxy or script forwarding partially-populated commands.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- ${fieldName} must use the broker binary protocol (pulsar://
- Invalid messageId base64 value
- Invalid messageId value
- Timeout during delete operation
- Timeout during close operation
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/e3c0bde26ca2d898.
Report an issue: GitHub.