OrchardCMS/OrchardCore · error · InvalidOperationException

Can't load for update a cached object

Error message

Can't load for update a cached object

What it means

DocumentManager<TDocument>.GetOrCreateMutableAsync loads a mutable document from the document store, then guards against the pathological case where the store returned the very same object instance that is currently cached in memory. Updating that shared instance would mutate the cached (shared) document, so it throws InvalidOperationException.

Solutions

  1. Fix the IDocumentStore implementation so GetOrCreateMutableAsync returns a fresh mutable instance, never the cached object.
  2. In tests, make the fake store return a new TDocument instance per call.
  3. If the document is not meant to be edited, use GetAsync/GetOrCreateAsync instead of the mutable variant.

Example fix

// before (fake store)
public Task<TDocument> GetOrCreateMutableAsync(Func<ValueTask<TDocument>> f) => Task.FromResult(_cachedDocument);
// after
public async Task<TDocument> GetOrCreateMutableAsync(Func<ValueTask<TDocument>> f)
{
    var doc = await f();
    return Clone(doc); // return a distinct mutable instance
}
Defensive patterns

Strategy: try-catch

Type guard

// Only mutate documents obtained from the mutable API:
bool IsMutable<T>(T doc, T cached) => !ReferenceEquals(doc, cached);

Try / catch

try { doc = await manager.GetOrCreateMutableAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("load for update a cached object")) {
    // fix or replace the IDocumentStore implementation; it must return a fresh instance
}

Prevention

When it happens

Trigger: Calling GetOrCreateMutableAsync when the document store's GetOrCreateMutableAsync returned the identical instance held in the memory cache — typically when a store implementation (or custom IDocumentStore) incorrectly returns the cached object instead of a clone/new mutable copy.

Common situations: Custom or mocked IDocumentStore implementations that skip mutable-cloning; misconfigured document store (e.g. a store sharing cache instances); tests that register a fake store returning the same document.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/cb47fbc43fa4e49c. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore.Infrastructure/Documents/DocumentManager.cs:73

                documentStore = (IDocumentStore)ShellScope.Services.GetRequiredService(DocumentStoreServiceType);
                ShellScope.Set(DocumentStoreServiceType, documentStore);
            }

            return documentStore;
        }
    }

    public async Task<TDocument> GetOrCreateMutableAsync(Func<Task<TDocument>> factoryAsync = null)
    {
        TDocument document;

        if (!_isVolatile)
        {
            document = await DocumentStore.GetOrCreateMutableAsync(factoryAsync);

            if (_memoryCache.TryGetValue<TDocument>(_options.CacheKey, out var cached) && document == cached)
            {
                throw new InvalidOperationException("Can't load for update a cached object");
            }
        }
        else
        {
            var volatileCache = ShellScope.Get<TDocument>(typeof(TDocument));
            if (volatileCache is not null)
            {
                document = volatileCache;
            }
            else
            {
                document = await GetFromDistributedCacheAsync()
                    ?? await (factoryAsync?.Invoke() ?? Task.FromResult((TDocument)null))
                    ?? new TDocument();

                ShellScope.Set(typeof(TDocument), document);
            }
        }

View on GitHub (pinned to 4306c0717f)