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
- Fix the IDocumentStore implementation so GetOrCreateMutableAsync returns a fresh mutable instance, never the cached object.
- In tests, make the fake store return a new TDocument instance per call.
- 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
- Never implement IDocumentStore.GetOrCreateMutableAsync by returning a cached instance
- In test fakes, return new instances per call
- Keep mutable and cached document lifetimes separate
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)