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

  1. Use GetOrCreateMutableAsync to obtain a mutable copy before editing, then UpdateAsync that copy.
  2. Clone the document before mutating if you must edit a value obtained from GetAsync.
  3. 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

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


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)