dotnet/orleans · error · InvalidOperationException
Load must be called after a failed Store before this storage
Error message
Load must be called after a failed Store before this storage instance can be reused.
What it means
Thrown by DynamoDBTransactionalStateStorage.Store when the requiresReload flag is still true. Store sets requiresReload = true at the start of its work because it mutates cached entities while building the batch; if that batch fails, the in-memory cache is left inconsistent. The contract is that after a failed Store the caller must call Load to rebuild the cache before reusing the instance. Calling Store again without Load violates that contract.
Source
Thrown at src/AWS/Orleans.Transactions.DynamoDB/TransactionalState/DynamoDBTransactionalStateStorage.cs:147
var metadata = this.key.Metadata is { Length: > 0 }
? this.ConvertFromStorageFormat<TransactionalStateMetaData>(this.key.Metadata)
: new TransactionalStateMetaData();
this.requiresReload = false;
return new TransactionalStorageLoadResponse<TState>(this.key.ETag.ToString(), committedState, this.key.CommittedSequenceId, metadata, PrepareRecordsToRecover);
}
catch (Exception ex)
{
this.logger.LogError(ex, "Error loading transactional state for partition key {PartitionKey}", this.partitionKey);
throw;
}
}
/// <inheritdoc />
public async Task<string> Store(string? expectedETag, TransactionalStateMetaData metadata, List<PendingTransactionState<TState>>? statesToPrepare, long? commitUpTo, long? abortAfter)
{
if (this.requiresReload)
{
throw new InvalidOperationException("Load must be called after a failed Store before this storage instance can be reused.");
}
var batchOperation = new BatchOperation(this.storage, this.tableName, this.key, this.logger);
var keyWasNew = !this.key.ETag.HasValue;
try
{
var keyETag = key.ETag?.ToString();
if ((!string.IsNullOrWhiteSpace(keyETag) || !string.IsNullOrWhiteSpace(expectedETag)) &&
keyETag != expectedETag)
{
throw new ArgumentException("Etag does not match", nameof(expectedETag));
}
var serializedMetadata = this.ConvertToStorageFormat(metadata);
var timestamp = DateTimeOffset.UtcNow;
var committedSequenceId = commitUpTo.HasValue && commitUpTo.Value > key.CommittedSequenceId
? commitUpTo.ValueView on GitHub (pinned to fca799fa70)
Solutions
- After any failed Store, call Load on the same instance before retrying Store.
- In retry logic, structure as: try Store; on failure -> await Load(); then retry Store.
- Do not catch-and-ignore Store failures; treat them as requiring a cache reload.
- If Load also fails, surface the error rather than looping on Store.
Example fix
// before
try { await store.Store(etag, meta, prep, commit, abort); }
catch { await store.Store(etag, meta, prep, commit, abort); } // throws: requiresReload
// after
try { await store.Store(etag, meta, prep, commit, abort); }
catch
{
await store.Load(); // restore consistent cache
await store.Store(etag, meta, prep, commit, abort); // safe to retry Defensive patterns
Strategy: try-catch
Try / catch
try { await store.Store(etag, meta, prep, commit, abort); }
catch (Exception ex) when (store is not null)
{
logger.LogWarning(ex, "Store failed; reloading cache before any retry.");
var fresh = await store.Load(); // clears requiresReload
await store.Store(fresh.ETag, meta, prep, commit, abort);
} Prevention
- After a failed Store, always call Load before retrying on the same instance.
- Do not catch-and-ignore Store failures.
- Structure retry as Store -> on failure Load -> Store.
- Surface persistent Load failures instead of looping.
When it happens
Trigger: A Store call failed (batch write error, size validation, etc.) and the same storage instance is reused for another Store without an intervening Load. The previous failure left requiresReload true to force a cache refresh.
Common situations: Retry loops that re-invoke Store on the same instance after a transient DynamoDB error; custom transaction participants that ignore Store failures and continue; tests that hammer Store without reloading.
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/da0694ddb3c9d821.
Report an issue: GitHub.