apache/beam · error · IllegalStateException
Received message missing ackId
Error message
Received message missing ackId
What it means
Each message returned by a Pub/Sub pull carries an ackId used to acknowledge/nack it. PubsubGrpcClient.pull throws IllegalStateException when the ReceivedMessage's ackId is empty, since without it the message can never be acknowledged and would be redelivered forever.
Source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/pubsub/PubsubGrpcClient.java:291
Map<String, String> attributes = pubsubMessage.getAttributes();
// Timestamp.
long timestampMsSinceEpoch;
if (timestampAttribute == null || timestampAttribute.isEmpty()) {
if (!pubsubMessage.hasPublishTime()) {
throw new IllegalStateException("Received message missing publishTime");
}
Timestamp timestampProto = pubsubMessage.getPublishTime();
timestampMsSinceEpoch =
timestampProto.getSeconds() * 1000 + timestampProto.getNanos() / 1000L / 1000L;
} else {
timestampMsSinceEpoch = extractTimestampAttribute(timestampAttribute, attributes);
}
// Ack id.
String ackId = message.getAckId();
if (ackId.isEmpty()) {
throw new IllegalStateException("Received message missing ackId");
}
// Record id, if any.
String recordId = null;
if (idAttribute != null) {
recordId = attributes.get(idAttribute);
}
if (recordId == null || recordId.isEmpty()) {
// Fall back to the Pubsub provided message id.
recordId = pubsubMessage.getMessageId();
}
incomingMessages.add(
IncomingMessage.of(
pubsubMessage, timestampMsSinceEpoch, requestTimeMsSinceEpoch, ackId, recordId));
}
return incomingMessages;
}View on GitHub (pinned to 12126d8942)
Solutions
- Fix the test mock/emulator to populate ackId on every ReceivedMessage
- Use the official Google Pub/Sub service or current client libraries
- Filter out invalid received messages before they reach Beam (proxy-side validation)
- Retry the pull; a transiently corrupted response is typically replaced by a valid one
Example fix
// mock before
ReceivedMessage.newBuilder().setMessage(msg).build();
// after
ReceivedMessage.newBuilder().setMessage(msg).setAckId("ack-123").build(); Defensive patterns
Strategy: try-catch
Validate before calling
if (receivedMessage.getAckId() == null || receivedMessage.getAckId().isEmpty()) {
throw new IllegalStateException("ReceivedMessage has empty ackId");
} Try / catch
try { messages = client.pull(subscription, deadline, batchSize, clock); } catch (IllegalStateException e) { log.error("Missing ackId: {}", e.getMessage()); } Prevention
- Use official Pub/Sub client libraries and service endpoints
- Populate ackId in any mocked pull responses
- Never strip ackId in proxies between Beam and Pub/Sub
- Log raw responses when integrating custom backends
When it happens
Trigger: pull() processes a ReceivedMessage whose getAckId() returns an empty string while reading from a subscription via PubsubIO.read through the gRPC client.
Common situations: Mocked or synthetic subscription responses in tests missing ackId; broken proxy/emulator implementations; corrupted streaming-pull responses from a non-standard backend.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Received message missing publishTime
- expansion service error: %s
- Pubsub message data field of length {payloadSize} exceeds ma
- Pubsub message ordering key of length {orderingKeySize} exce
- Pubsub message must contain a non-empty payload or at least
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/7d3409be5e1fbf42.
Report an issue: GitHub.