dotnet/orleans · error · InvalidOperationException
Only DynamoDB put and delete operations are supported.
Error message
Only DynamoDB put and delete operations are supported.
What it means
Thrown by the static BatchOperation.GetOperationSize helper when a TransactWriteItem is neither a Put (with an Item) nor a Delete. The DynamoDB transactional batch only ever queues puts and deletes, so any other TransactWriteItem type (Update, ConditionCheck) hits this guard. In practice this is an internal invariant violation, not a normal user path, because all callers (Add methods) construct Put/Delete items.
Source
Thrown at src/AWS/Orleans.Transactions.DynamoDB/TransactionalState/DynamoDBTransactionalStateStorage.cs:687
private static bool IsStorageConflict(TransactionCanceledException exception)
{
return exception.CancellationReasons?.Any(
reason => string.Equals(reason.Code, "ConditionalCheckFailed", StringComparison.Ordinal)) is true
|| exception.Message.Contains("ConditionalCheckFailed", StringComparison.Ordinal);
}
private static int GetOperationSize(TransactWriteItem operation, int? affectedItemSize)
{
if (operation.Put?.Item is { } item)
{
return ValidateItemSize(item, "state");
}
if (operation.Delete is not null)
{
return affectedItemSize ?? MaxDynamoDBItemSize;
}
throw new InvalidOperationException("Only DynamoDB put and delete operations are supported.");
}
}
[LoggerMessage(
Level = LogLevel.Debug,
Message = "{Partition} Loaded v0, fresh"
)]
private partial void LogDebugLoadedV0Fresh(string partition);
[LoggerMessage(
Level = LogLevel.Critical,
Message = "{Partition} {Error}"
)]
private partial void LogCriticalPartitionError(string partition, string error);
[LoggerMessage(
Level = LogLevel.Error,
Message = "{Message}"View on GitHub (pinned to fca799fa70)
Solutions
- Stop feeding non-Put/non-Delete TransactWriteItems to the batch; DynamoDB TransactWriteItems in this provider are modeled as put-or-delete.
- If you genuinely need Update/ConditionCheck, extend GetOperationSize (and GetItemSize) to account for their size and add the corresponding branch.
Example fix
// before: a custom op path pushes an Update
batch.Add(new TransactWriteItem { Update = updateExpr }, ...);
// after: express the change as a Put of the fully-formed item, as the provider expects
batch.Add(new TransactWriteItem { Put = new Put { TableName = table, Item = fullItem } }, ...); Defensive patterns
Strategy: type-guard
Validate before calling
// Only ever enqueue Put/Delete items, which is what the provider supports
static TransactWriteItem AsPut(string table, Dictionary<string,AttributeValue> item) =>
new() { Put = new Put { TableName = table, Item = item } }; Type guard
static bool IsSupportedBatchOp(TransactWriteItem op) => op.Put?.Item is not null || op.Delete is not null;
Try / catch
try { await grain.TxUpdate(); }
catch (InvalidOperationException ix) when (ix.Message.Contains("Only DynamoDB put and delete operations are supported"))
{
_logger.LogCritical("Internal: unsupported TransactWriteItem pushed into the transactional batch");
throw;
} Prevention
- Always construct TransactWriteItems as Put or Delete in custom provider extensions.
- If you need Update/ConditionCheck, extend GetOperationSize and GetItemSize rather than bypassing them.
- Avoid reflection-based code that injects arbitrary TransactWriteItem types into the batch.
- Unit-test custom operation construction against IsSupportedBatchOp before enqueueing.
When it happens
Trigger: Produced if code adds a TransactWriteItem whose Put is null and whose Delete is null to the BatchOperation (via reflection, a fork/patch, or a future code path that uses Update/ConditionCheck). Triggered by an internal programming error or a custom extension that injects a different operation type.
Common situations: Custom fork of the provider adding Update/ConditionCheck operations without updating GetOperationSize; reflection-based unit tests feeding arbitrary items; an upgrade that changed how operations are constructed.
Related errors
- The transactional state storage provider name is required.
- Configuration for DynamoDBTransactionalStateStorage {this.na
- Configuration for DynamoDBTransactionalStateStorage {this.na
- Configuration for DynamoDBTransactionalStateStorage {this.na
- Storage state corrupted: no record for committed state v{thi
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/61dcbc74bdf63e96.
Report an issue: GitHub.