{"record":{"id":"cb47fbc43fa4e49c","repo":"OrchardCMS/OrchardCore","slug":"can-t-load-for-update-a-cached-object","errorCode":null,"errorMessage":"Can't load for update a cached object","messagePattern":"Can't load for update a cached object","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.Infrastructure/Documents/DocumentManager.cs","lineNumber":73,"sourceCode":"                documentStore = (IDocumentStore)ShellScope.Services.GetRequiredService(DocumentStoreServiceType);\n                ShellScope.Set(DocumentStoreServiceType, documentStore);\n            }\n\n            return documentStore;\n        }\n    }\n\n    public async Task<TDocument> GetOrCreateMutableAsync(Func<Task<TDocument>> factoryAsync = null)\n    {\n        TDocument document;\n\n        if (!_isVolatile)\n        {\n            document = await DocumentStore.GetOrCreateMutableAsync(factoryAsync);\n\n            if (_memoryCache.TryGetValue<TDocument>(_options.CacheKey, out var cached) && document == cached)\n            {\n                throw new InvalidOperationException(\"Can't load for update a cached object\");\n            }\n        }\n        else\n        {\n            var volatileCache = ShellScope.Get<TDocument>(typeof(TDocument));\n            if (volatileCache is not null)\n            {\n                document = volatileCache;\n            }\n            else\n            {\n                document = await GetFromDistributedCacheAsync()\n                    ?? await (factoryAsync?.Invoke() ?? Task.FromResult((TDocument)null))\n                    ?? new TDocument();\n\n                ShellScope.Set(typeof(TDocument), document);\n            }\n        }","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.Infrastructure/Documents/DocumentManager.cs#L55-L91","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (fake store)\npublic Task<TDocument> GetOrCreateMutableAsync(Func<ValueTask<TDocument>> f) => Task.FromResult(_cachedDocument);\n// after\npublic async Task<TDocument> GetOrCreateMutableAsync(Func<ValueTask<TDocument>> f)\n{\n    var doc = await f();\n    return Clone(doc); // return a distinct mutable instance\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// Only mutate documents obtained from the mutable API:\nbool IsMutable<T>(T doc, T cached) => !ReferenceEquals(doc, cached);","tryCatchPattern":"try { doc = await manager.GetOrCreateMutableAsync(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"load for update a cached object\")) {\n    // fix or replace the IDocumentStore implementation; it must return a fresh instance\n}","preventionTips":["Never implement IDocumentStore.GetOrCreateMutableAsync by returning a cached instance","In test fakes, return new instances per call","Keep mutable and cached document lifetimes separate"],"tags":["documents","caching","internal-invariant-violation"],"backgroundTag":"internal-invariant-violation","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}