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.Value

View on GitHub (pinned to fca799fa70)

Solutions

  1. After any failed Store, call Load on the same instance before retrying Store.
  2. In retry logic, structure as: try Store; on failure -> await Load(); then retry Store.
  3. Do not catch-and-ignore Store failures; treat them as requiring a cache reload.
  4. 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

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


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/da0694ddb3c9d821. Report an issue: GitHub.