{"record":{"id":"93568635424b500a","repo":"apache/beam","slug":"expected-state-to-be-pending-or-started-but-was-complete-935686","errorCode":null,"errorMessage":"Expected state to be PENDING or STARTED, but was COMPLETE_ERROR","messagePattern":"Expected state to be PENDING or STARTED, but was COMPLETE_ERROR","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/firestore/RpcQosImpl.java","lineNumber":192,"sourceCode":"        Sum.ofLongs());\n  }\n\n  private enum AttemptState {\n    PENDING,\n    STARTED,\n    COMPLETE_SUCCESS,\n    COMPLETE_ERROR;\n\n    public void checkActive() {\n      switch (this) {\n        case PENDING:\n        case STARTED:\n          return;\n        case COMPLETE_SUCCESS:\n          throw new IllegalStateException(\n              \"Expected state to be PENDING or STARTED, but was COMPLETE_SUCCESS\");\n        case COMPLETE_ERROR:\n          throw new IllegalStateException(\n              \"Expected state to be PENDING or STARTED, but was COMPLETE_ERROR\");\n      }\n    }\n\n    public void checkStarted() {\n      switch (this) {\n        case STARTED:\n          return;\n        case PENDING:\n          throw new IllegalStateException(\"Expected state to be STARTED, but was PENDING\");\n        case COMPLETE_SUCCESS:\n          throw new IllegalStateException(\"Expected state to be STARTED, but was COMPLETE_SUCCESS\");\n        case COMPLETE_ERROR:\n          throw new IllegalStateException(\"Expected state to be STARTED, but was COMPLETE_ERROR\");\n      }\n    }\n  }\n","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/firestore/RpcQosImpl.java#L174-L210","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntry {\n  attempt.checkActive();\n  doRpc();\n} catch (Exception e) {\n  attempt.completeWithError(e);\n}\nattempt.checkActive(); // throws: attempt already failed\n// after\ntry {\n  attempt.checkActive();\n  doRpc();\n} catch (Exception e) {\n  attempt.completeWithError(e);\n}\nattempt = qos.newAttempt(); // new attempt for the retry\nattempt.checkActive();","handlingStrategy":"retry","validationCode":"// Java: only retry with a fresh attempt after completeWithError()\ntry {\n  attempt.checkActive();\n} catch (IllegalStateException e) {\n  attempt = qos.newAttempt();\n}","typeGuard":"boolean attemptActive(RpcQos.Attempt attempt) {\n  try { attempt.checkActive(); return true; }\n  catch (IllegalStateException e) { return false; }\n}","tryCatchPattern":"try {\n  attempt.checkActive();\n  doRpc();\n} catch (Exception rpcError) {\n  attempt.completeWithError(rpcError);\n  attempt = qos.newAttempt(); // fresh attempt for retry\n  attempt.start();\n}","preventionTips":["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."],"tags":["firestore","qos","illegal-state","lifecycle","java","apache-beam"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}