apache/iceberg · error · IllegalStateException

Dynamo catalog table

Error message

Dynamo catalog table %s is not active, current status: %s

What it means

DynamoDbCatalog.checkTableActive() calls DescribeTable on the underlying DynamoDB catalog table and requires its status to be ACTIVE before performing catalog reads/writes. If the status differs (e.g. CREATING, UPDATING, DELETING), an IllegalStateException with this message is thrown because the catalog table cannot reliably serve requests.

Solutions

  1. Wait until `aws dynamodb describe-table --table-name <catalog-table>` reports ACTIVE before initializing the catalog; add retry/backoff around catalog initialization.
  2. Ensure the setup script does not delete/recreate the DynamoDB catalog table while clients are running.
  3. Catch IllegalStateException from catalog construction and retry after a delay if the status is transitioning (CREATING/UPDATING).

Example fix

// before
DynamoDbCatalog catalog = new DynamoDbCatalog();
catalog.initialize(config); // may hit non-ACTIVE table

// after
waitUntilTableActive(catalogTableName); // poll DescribeTable until status == ACTIVE
DynamoDbCatalog catalog = new DynamoDbCatalog();
catalog.initialize(config);
Defensive patterns

Strategy: retry

Validate before calling

DescribeTableResponse r = dynamo.describeTable(DescribeTableRequest.builder().tableName(catalogTable).build());
if (!r.table().tableStatus().equals(TableStatus.ACTIVE)) {
  throw new IllegalStateException("Catalog table " + catalogTable + " is " + r.table().tableStatus());
}

Try / catch

await.atMost(5, MINUTES).pollInterval(10, SECONDS).until(() -> {
  try { catalog.initialize(config); return true; }
  catch (IllegalStateException e) { return false; } // table still transitioning
});

Prevention

When it happens

Trigger: Any catalog operation that routes through checkTableActive while the DynamoDB catalog table is mid-update — table creation, deletion, or a TTL/throughput change leaving it in CREATING/UPDATING/DELETING state.

Common situations: Deployment scripts that delete and immediately recreate the catalog table, then start the catalog before the new table is ACTIVE; on-demand capacity mode switches; running tests against a table still being provisioned.

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/0b6a2b6d03c29726. Report an issue: GitHub.

Appendix: source

Thrown at aws/src/main/java/org/apache/iceberg/aws/dynamodb/DynamoDbCatalog.java:661

                    .build())
            .billingMode(BillingMode.PAY_PER_REQUEST)
            .build());

    // wait for the dynamo table to complete provisioning, which takes around 10 seconds
    Tasks.foreach(awsProperties.dynamoDbTableName())
        .retry(CATALOG_TABLE_CREATION_WAIT_ATTEMPTS_MAX)
        .throwFailureWhenFinished()
        .onlyRetryOn(IllegalStateException.class)
        .run(this::checkTableActive);
  }

  private void checkTableActive(String tableName) {
    try {
      DescribeTableResponse response =
          dynamo.describeTable(DescribeTableRequest.builder().tableName(tableName).build());
      TableStatus currentStatus = response.table().tableStatus();
      if (!currentStatus.equals(TableStatus.ACTIVE)) {
        throw new IllegalStateException(
            String.format(
                "Dynamo catalog table %s is not active, current status: %s",
                tableName, currentStatus));
      }
    } catch (ResourceNotFoundException e) {
      throw new IllegalStateException(
          String.format("Cannot find Dynamo catalog table %s", tableName));
    }
  }

  private boolean updateProperties(
      Namespace namespace,
      String updateExpression,
      Map<String, AttributeValue> attributeValues,
      Map<String, String> attributeNames) {
    validateNamespace(namespace);
    Map<String, AttributeValue> key = namespacePrimaryKey(namespace);
    try {

View on GitHub (pinned to 86d9c8fc54)