dotnet/orleans · error · InvalidOperationException
Table {tableDescription.TableName} has a status of {tableDes
Error message
Table {tableDescription.TableName} has a status of {tableDescription.TableStatus} and can't be updated automatically. What it means
Thrown by DynamoDBStorage.UpdateTableAsync when the existing table's TableStatus is not one of CREATING, UPDATING, or ACTIVE (the set held by _updateTableValidTableStatuses) and updateIfExists is true. The library only auto-updates tables it considers healthy/transitioning; statuses like DELETING, ARCHIVED, or INACCESSIBLE_ENCRYPTION_CREDENTIALS are rejected so it does not fight an in-flight lifecycle operation.
Source
Thrown at src/AWS/Shared/Storage/DynamoDBStorage.cs:267
}
catch (Exception exc)
{
LogErrorCouldNotCreateTable(_logger, exc, tableName);
throw;
}
}
private async ValueTask UpdateTableAsync(TableDescription tableDescription, List<AttributeDefinition> attributes, List<GlobalSecondaryIndex>? secondaryIndexes = null, string? ttlAttributeName = null, CancellationToken cancellationToken = default)
{
if (!this._updateIfExists)
{
LogWarningTableNotUpdated(_logger, tableDescription.TableName);
return;
}
if (!_updateTableValidTableStatuses.Contains(tableDescription.TableStatus))
{
throw new InvalidOperationException($"Table {tableDescription.TableName} has a status of {tableDescription.TableStatus} and can't be updated automatically.");
}
if (tableDescription.TableStatus == TableStatus.CREATING
|| tableDescription.TableStatus == TableStatus.UPDATING)
{
tableDescription = await TableWaitOnStatusAsync(tableDescription.TableName, tableDescription.TableStatus, TableStatus.ACTIVE, cancellationToken: cancellationToken);
}
var request = new UpdateTableRequest
{
TableName = tableDescription.TableName,
AttributeDefinitions = attributes,
BillingMode = this._useProvisionedThroughput ? BillingMode.PROVISIONED : BillingMode.PAY_PER_REQUEST,
ProvisionedThroughput = _provisionedThroughput,
GlobalSecondaryIndexUpdates = this._useProvisionedThroughput
? tableDescription.GlobalSecondaryIndexes?.Select(gsi => new GlobalSecondaryIndexUpdate
{
Update = new UpdateGlobalSecondaryIndexActionView on GitHub (pinned to fca799fa70)
Solutions
- Wait for the table to return to ACTIVE (or finish DELETING) and restart the silo, or recreate the table.
- If the table is in a bad state (ARCHIVED / INACCESSIBLE_ENCRYPTION_CREDENTIALS), resolve the underlying AWS issue (restore KMS key access, restore the table).
- If you intentionally manage the schema out-of-band, set UpdateIfExists = false so the provider does not attempt auto-update.
- Confirm no concurrent operator/script is deleting or archiving the table during deployments.
Example fix
// before: provider tries to update a table that is DELETING/ARCHIVED opt.UpdateIfExists = true; // after: either let the table settle to ACTIVE, or disable auto-update opt.UpdateIfExists = false; // you manage schema out-of-band // then ensure the table is ACTIVE before the silo starts
Defensive patterns
Strategy: validation
Validate before calling
// Before startup, ensure the target table is in an updatable status (or absent so it can be created)
var statuses = new[] { TableStatus.CREATING, TableStatus.UPDATING, TableStatus.ACTIVE };
var desc = await DescribeExistingTableAsync(tableName);
if (desc is not null && !statuses.Contains(desc.TableStatus) && opt.UpdateIfExists)
throw new InvalidOperationException($"Table {tableName} is {desc.TableStatus}; resolve before starting the silo"); Type guard
static bool IsUpdatableStatus(TableStatus s) =>
s == TableStatus.CREATING || s == TableStatus.UPDATING || s == TableStatus.ACTIVE; Try / catch
try { await silo.StartAsync(); }
catch (InvalidOperationException ix) when (ix.Message.Contains("can't be updated automatically"))
{
_logger.LogCritical("DynamoDB table is in a non-updatable status; resolve (restore/delete) or set UpdateIfExists=false");
throw;
} Prevention
- Do not delete/archive a table out-of-band while the silo is starting.
- If you manage schema manually, set UpdateIfExists = false.
- Resolve KMS/encryption issues before reusing an ARCHIVED or INACCESSIBLE table.
- Coordinate deployments so only one operator touches the table lifecycle.
When it happens
Trigger: Produced during provider init when the table already exists, UpdateIfExists is true, and DescribeTable returns a status outside the valid set. Triggered by a table currently being deleted, an archived table being reused, or a table whose KMS encryption key became inaccessible.
Common situations: Reusing a table that someone started deleting out-of-band; pointing the provider at an archived table; a KMS key rotation that left the table INACCESSIBLE_ENCRYPTION_CREDENTIALS; manual table operations colliding with silo startup.
Related errors
- service
- Table {tableName} has failed to reach the desired status of
- Configuration for DynamoDBGrainStorage {name} is invalid. Ta
- Configuration for DynamoDBGrainStorage {name} is invalid. Re
- Configuration for DynamoDBGrainStorage {name} is invalid. Wr
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/9f17e13bf535c98e.
Report an issue: GitHub.