{"record":{"id":"d2d6cf5e44082593","repo":"apache/beam","slug":"expected-state-to-be-started-but-was-pending","errorCode":null,"errorMessage":"Expected state to be STARTED, but was PENDING","messagePattern":"Expected state to be STARTED, but was PENDING","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":202,"sourceCode":"      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\n  private abstract class BaseRpcAttempt implements RpcAttempt {\n    private final Logger logger;\n    final O11y o11y;\n    final StatusCodeAwareBackoff backoff;\n    final Sleeper sleeper;\n\n    AttemptState state;\n    Instant start;\n\n    @SuppressWarnings(","sourceCodeStart":184,"sourceCodeEnd":220,"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#L184-L220","documentation":"AttemptState.checkStarted() asserts that an attempt has actually begun (STARTED) before an operation proceeds. Calling it while the attempt is still PENDING (start() was never invoked) throws IllegalStateException. The Qos framework requires start() to be called — which also reserves budget — before RPC work is attempted.","triggerScenarios":"Calling attempt.checkStarted() (or APIs that call it internally, like the write batch path) on an attempt created with qos.newAttempt() but never started with attempt.start().","commonSituations":"Skipping the start() call when writing custom Firestore sink code; refactoring that removed the start() invocation; early exits between newAttempt() and start() followed by an attempt to use the batch.","solutions":["Call attempt.start() after newAttempt() and before using the attempt for RPC operations.","Ensure no early return skips start() between creation and first use of the attempt.","Use the connector's high-level helpers (e.g. FirestoreV1 write transforms) which manage the attempt lifecycle for you.","If using checkActive() vs checkStarted() deliberately, switch to checkActive() when PENDING is an acceptable state."],"exampleFix":"// before\nRpcQos.Attempt attempt = qos.newAttempt();\nattempt.checkStarted(); // throws: still PENDING\n// after\nRpcQos.Attempt attempt = qos.newAttempt();\nattempt.start();\nattempt.checkStarted(); // OK","handlingStrategy":"validation","validationCode":"// Java: ensure start() was called before checkStarted()\nRpcQos.Attempt attempt = qos.newAttempt();\nattempt.start(); // must precede checkStarted()/RPC use\nattempt.checkStarted();","typeGuard":"boolean attemptStarted = false; // track alongside the attempt\nboolean readyForRpc(RpcQos.Attempt attempt) {\n  return attemptStarted;\n}","tryCatchPattern":"try {\n  attempt.checkStarted();\n  // perform batched write / RPC\n} catch (IllegalStateException e) {\n  LOG.error(\"Attempt was not started; call attempt.start() after newAttempt()\", e);\n}","preventionTips":["Always call start() immediately after newAttempt().","Avoid early returns between newAttempt() and start().","Prefer built-in FirestoreV1 transforms that manage the attempt lifecycle."],"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"}