{"record":{"id":"b86eb60e01167232","repo":"apache/beam","slug":"cannot-call-refreshschema-after-the-object-has-been-stopped","errorCode":null,"errorMessage":"Cannot call refreshSchema after the object has been stopped!","messagePattern":"Cannot call refreshSchema after the object has been stopped!","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/TableSchemaCache.java","lineNumber":220,"sourceCode":"    final String key = tableKey(tableReference);\n    Optional<SchemaHolder> existing =\n        runUnderMonitor(\n            () ->\n                Optional.ofNullable(\n                    this.cachedSchemas.putIfAbsent(key, SchemaHolder.of(tableSchema, 0))));\n    return existing.map(SchemaHolder::getTableSchema).orElse(null);\n  }\n\n  public void refreshSchema(\n      TableReference tableReference,\n      DatasetService datasetService,\n      BigQueryServices.WriteStreamService writeStreamService,\n      BigQueryOptions options) {\n    int targetVersion =\n        runUnderMonitor(\n            () -> {\n              if (stopped) {\n                throw new RuntimeException(\n                    \"Cannot call refreshSchema after the object has been stopped!\");\n              }\n              String key = tableKey(tableReference);\n              @Nullable SchemaHolder schemaHolder = cachedSchemas.get(key);\n              int nextVersion = schemaHolder != null ? schemaHolder.getVersion() + 1 : 0;\n              @Nullable Refresh existing =\n                  tablesToRefresh.putIfAbsent(\n                      key, Refresh.of(datasetService, writeStreamService, options, nextVersion));\n              // Wait at least until the next version.\n              return (existing == null) ? nextVersion : existing.getTargetVersion();\n            });\n    waitForRefresh(tableReference, targetVersion);\n  }\n\n  private void waitForRefresh(TableReference tableReference, int version) {\n    tableUpdateMonitor.enterWhenUninterruptibly(\n        new Guard(tableUpdateMonitor) {\n          @Override","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/TableSchemaCache.java#L202-L238","documentation":"TableSchemaCache.refreshSchema schedules a schema refresh for a table, but the cache object has a stopped flag that permanently disables it after shutdown. Calling refreshSchema after stop() throws this RuntimeException to prevent use of a torn-down resource.","triggerScenarios":"Calling refreshSchema on a TableSchemaCache after its stop() method has been invoked, e.g. during pipeline teardown, a late retry, or an element still being processed while the writer is closing.","commonSituations":"DoFn teardown racing with pending schema refresh calls, reusing a cached writer/schema-cache object across bundle or worker lifetimes, or error-handling code that retries refresh after the stream was aborted.","solutions":["Ensure no refreshSchema calls happen after stop(); gate the call with a stopped/isAlive check","Fix lifecycle ordering so all schema refreshes complete before stop() is called","Do not retry refreshSchema after a failure during teardown; treat stop as terminal","Create a new TableSchemaCache instance if you legitimately need to refresh again","Guard with try-catch and log if the call is best-effort during shutdown"],"exampleFix":"// before\nschemaCache.refreshSchema(tableReference, writeStreamService, options);\n// after\nif (!schemaCache.isStopped()) {\n  schemaCache.refreshSchema(tableReference, writeStreamService, options);\n}","handlingStrategy":"try-catch","validationCode":"if (cacheStopped) { throw new IllegalStateException(\"refreshSchema called after stop()\"); } // check before calling","typeGuard":null,"tryCatchPattern":"try {\n  schemaCache.refreshSchema(tableRef, writeStreamService, options);\n} catch (RuntimeException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"after the object has been stopped\")) {\n    LOG.warn(\"Ignoring refresh after stop\"); // best-effort during teardown\n  } else throw e;\n}","preventionTips":["Treat stop() as terminal; never retry refresh afterwards","Complete all schema refreshes before teardown","Track cache lifecycle explicitly in your writer code","Avoid caching TableSchemaCache beyond its owning DoFn lifetime"],"tags":["java","google-bigquery","lifecycle","illegal-state"],"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-20T03:17:13.778Z"}