OrchardCMS/OrchardCore · error · InvalidOperationException
Can't update a cached object
Error message
Can't update a cached object
What it means
DocumentManager<TDocument>.UpdateAsync persists a mutated document but first checks whether the instance being saved is the exact same object as the one in the memory cache. Saving the cached instance would corrupt the shared cache, so it throws InvalidOperationException and refuses the update.
Solutions
- Use GetOrCreateMutableAsync to obtain a mutable copy before editing, then UpdateAsync that copy.
- Clone the document before mutating if you must edit a value obtained from GetAsync.
- Never mutate documents returned by the read path; treat them as immutable shared instances.
Example fix
// before var doc = await documentManager.GetAsync(); doc.Settings.Enabled = true; await documentManager.UpdateAsync(doc); // cached instance // after var mutable = await documentManager.GetOrCreateMutableAsync(); mutable.Settings.Enabled = true; await documentManager.UpdateAsync(mutable);
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the doc is not the cached instance before updating: _memoryCache.TryGetValue<TDocument>(cacheKey, out var cached); if (ReferenceEquals(doc, cached)) doc = Clone(doc);
Type guard
bool IsSafeToUpdate<T>(T doc, T cached) => !ReferenceEquals(doc, cached);
Try / catch
try { await manager.UpdateAsync(doc); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Can't update a cached object")) {
// re-fetch via GetOrCreateMutableAsync and reapply changes
} Prevention
- Treat documents from GetAsync as immutable
- Always use GetOrCreateMutableAsync for read-modify-write flows
- Clone before mutating when unsure of instance provenance
When it happens
Trigger: Calling UpdateAsync with the same document instance currently stored under the manager's CacheKey — e.g. mutating and re-saving the object obtained from GetAsync without going through GetOrCreateMutableAsync.
Common situations: Developer fetches the cached document via GetAsync, mutates it, and calls UpdateAsync on it; custom cache/store setups where identity equality holds between fetched and cached objects.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Can't load for update a cached object
- AlternateCollection can't be changed.
- Invalid serial number for shell descriptor
- Unable to remove system-defined type.
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/b239e6e8f32e2d4a.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore.Infrastructure/Documents/DocumentManager.cs:179
if (_isDistributed)
{
try
{
_ = await _distributedCache.GetStringAsync(_options.CacheIdKey);
}
catch
{
await DocumentStore.CancelAsync();
_logger.LogError("Can't update the '{DocumentName}' if not able to access the distributed cache", typeof(TDocument).Name);
throw;
}
}
if (_memoryCache.TryGetValue<TDocument>(_options.CacheKey, out var cached) && document == cached)
{
throw new InvalidOperationException("Can't update a cached object");
}
document.Identifier ??= IdGenerator.GenerateId();
if (!_isVolatile)
{
await DocumentStore.UpdateAsync(document, async document =>
{
// A non volatile document can be invalidated.
await InvalidateInternalAsync(document);
if (afterUpdateAsync != null)
{
await afterUpdateAsync(document);
}
},
_options.CheckConcurrency.Value);
View on GitHub (pinned to 4306c0717f)