{"record":{"id":"87e5b09ad767e069","repo":"apache/beam","slug":"spannerexception-rethrown-from-operation-failure-timeout","errorCode":null,"errorMessage":"SpannerException (rethrown from operation failure/timeout cause)","messagePattern":"SpannerException \\(rethrown from operation failure/timeout cause\\)","errorType":"exception","errorClass":"SpannerException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/changestreams/dao/PartitionMetadataAdminDao.java","lineNumber":255,"sourceCode":"              + \" ON \"\n              + names.getTableName()\n              + \" (\"\n              + COLUMN_CREATED_AT\n              + \",\"\n              + COLUMN_START_TIMESTAMP\n              + \")\");\n    }\n    OperationFuture<Void, UpdateDatabaseDdlMetadata> op =\n        databaseAdminClient.updateDatabaseDdl(instanceId, databaseId, ddl, null);\n    try {\n      // Initiate the request which returns an OperationFuture.\n      op.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);\n    } catch (ExecutionException | TimeoutException e) {\n      // If the operation failed or timed out during execution, expose the cause.\n      if (e.getCause() != null) {\n        throw (SpannerException) e.getCause();\n      } else {\n        throw SpannerExceptionFactory.asSpannerException(e);\n      }\n    } catch (InterruptedException e) {\n      // Throw when a thread is waiting, sleeping, or otherwise occupied,\n      // and the thread is interrupted, either before or during the activity.\n      throw SpannerExceptionFactory.propagateInterrupt(e);\n    }\n  }\n\n  /**\n   * Drops the metadata table. This operation should complete in {@link\n   * PartitionMetadataAdminDao#TIMEOUT_MINUTES} minutes.\n   */\n  public void deletePartitionMetadataTable(List<String> indexes) {\n    List<String> ddl = new ArrayList<>();\n    if (this.isPostgres()) {\n      indexes.forEach(index -> ddl.add(\"DROP INDEX \\\"\" + index + \"\\\"\"));\n      ddl.add(\"DROP TABLE \\\"\" + names.getTableName() + \"\\\"\");\n    } else {","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/changestreams/dao/PartitionMetadataAdminDao.java#L237-L273","documentation":"PartitionMetadataAdminDao.createPartitionMetadataTable issues a Cloud Spanner admin operation and blocks on its result. If the operation fails or times out, the underlying SpannerException cause is rethrown so callers see a typed Spanner error; if there is no cause, the exception is wrapped via SpannerExceptionFactory. An InterruptedException is converted to an interrupted SpannerException without swallowing the interrupt flag.","triggerScenarios":"Calling createPartitionMetadataTable when the Spanner admin CreateDatabase/UpdateDatabaseDdl operation fails, exceeds TIMEOUT_MINUTES (5 minutes), or the thread is interrupted while waiting on op.get().","commonSituations":"Spanner database out of quota or DDL timeout; transient Spanner outages during pipeline bootstrap; permissions missing for spanner.databases.create; slow schema update queuing on a busy database.","solutions":["Inspect the rethrown SpannerException's code (e.g. RESOURCE_EXHAUSTED, DEADLINE_EXCEEDED, PERMISSION_DENIED) and fix the corresponding Spanner-side issue.","Retry the table creation; DDL operations are idempotent if you check for ALREADY_EXISTS.","Verify the service account has spanner.admin / database creation IAM roles.","Check Spanner instance health/quota if timeouts recur."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-check IAM and instance before creating the table\nboolean canCreate = spanner.getDatabaseAdminClient()\n    .listDatabases(instanceId).getValues().stream()\n    .noneMatch(db -> db.getId().getDatabase().equals(databaseId));","typeGuard":"if (!(e.getCause() instanceof SpannerException)) { e = SpannerExceptionFactory.asSpannerException(e); }","tryCatchPattern":"try { dao.createPartitionMetadataTable(); } catch (SpannerException e) {\n  if (e.getErrorCode() == ErrorCode.ALREADY_EXISTS) { /* ok */ }\n  else if (e.isRetryable()) { backoffRetry(); }\n  else { throw e; }\n}","preventionTips":["Grant spanner.admin roles to the pipeline service account beforehand.","Check instance quota/health before launching change-stream pipelines.","Treat table creation as idempotent (handle ALREADY_EXISTS)."],"tags":["gcp","spanner","database","timeout"],"backgroundTag":"database-write-failed","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"}