apache/beam · error · IllegalStateException
Expected state to be PENDING or STARTED, but was COMPLETE_ER
Error message
Expected state to be PENDING or STARTED, but was COMPLETE_ERROR
What it means
AttemptState.checkActive() throws IllegalStateException when the attempt already ended with COMPLETE_ERROR. Once an attempt has been marked failed via completeWithError(), it can no longer be treated as active. This enforces the Qos attempt state machine so failed attempts cannot consume budget or proceed.
Source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/firestore/RpcQosImpl.java:192
Sum.ofLongs());
}
private enum AttemptState {
PENDING,
STARTED,
COMPLETE_SUCCESS,
COMPLETE_ERROR;
public void checkActive() {
switch (this) {
case PENDING:
case STARTED:
return;
case COMPLETE_SUCCESS:
throw new IllegalStateException(
"Expected state to be PENDING or STARTED, but was COMPLETE_SUCCESS");
case COMPLETE_ERROR:
throw new IllegalStateException(
"Expected state to be PENDING or STARTED, but was COMPLETE_ERROR");
}
}
public void checkStarted() {
switch (this) {
case STARTED:
return;
case PENDING:
throw new IllegalStateException("Expected state to be STARTED, but was PENDING");
case COMPLETE_SUCCESS:
throw new IllegalStateException("Expected state to be STARTED, but was COMPLETE_SUCCESS");
case COMPLETE_ERROR:
throw new IllegalStateException("Expected state to be STARTED, but was COMPLETE_ERROR");
}
}
}
View on GitHub (pinned to 12126d8942)
Solutions
- Create a fresh attempt with qos.newAttempt() before retrying after an error.
- Call checkActive()/checkStarted() only at the beginning of an attempt's lifecycle, before any completion call.
- Ensure completeWithError() is called exactly once and the attempt is abandoned afterward.
- Serialize access to the Attempt if it is shared across threads.
Example fix
// before
try {
attempt.checkActive();
doRpc();
} catch (Exception e) {
attempt.completeWithError(e);
}
attempt.checkActive(); // throws: attempt already failed
// after
try {
attempt.checkActive();
doRpc();
} catch (Exception e) {
attempt.completeWithError(e);
}
attempt = qos.newAttempt(); // new attempt for the retry
attempt.checkActive(); Defensive patterns
Strategy: retry
Validate before calling
// Java: only retry with a fresh attempt after completeWithError()
try {
attempt.checkActive();
} catch (IllegalStateException e) {
attempt = qos.newAttempt();
} Type guard
boolean attemptActive(RpcQos.Attempt attempt) {
try { attempt.checkActive(); return true; }
catch (IllegalStateException e) { return false; }
} Try / catch
try {
attempt.checkActive();
doRpc();
} catch (Exception rpcError) {
attempt.completeWithError(rpcError);
attempt = qos.newAttempt(); // fresh attempt for retry
attempt.start();
} Prevention
- After completeWithError(), always create a new attempt before retrying.
- Call complete* exactly once per attempt.
- Wrap retry loops so each iteration owns its own Attempt.
When it happens
Trigger: Calling checkActive() on an attempt after an RPC failed and completeWithError() was invoked — e.g. retry logic that continues to use the same failed attempt instead of creating a new one.
Common situations: Retry loops that catch an exception but keep using the old Attempt object; rethrow paths that double-complete an attempt; concurrent completion and check from different threads.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Expected state to be PENDING or STARTED, but was COMPLETE_SU
- Expected state to be STARTED, but was PENDING
- Expected state to be STARTED, but was COMPLETE_SUCCESS
- Error collections cannot be added after Error Handler is clo
- ErrorHandler must be finalized before the output can be retu
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/93568635424b500a.
Report an issue: GitHub.