{"record":{"id":"d4f7e251438c75c1","repo":"stride3d/stride","slug":"using-the-cache-requires-a-database","errorCode":null,"errorMessage":"Using the cache requires a database.","messagePattern":"Using the cache requires a database\\.","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Effects/Compiler/EffectCompilerCache.cs","lineNumber":56,"sourceCode":"        // Used when shader is compiled (esp. when CompileEffectAsynchronously is false, but also when true during some specific race conditions)\n        private readonly Dictionary<ObjectId, EffectBytecodeCompilerResult> compiledShaders = new Dictionary<ObjectId, EffectBytecodeCompilerResult>();\n\n        private readonly DatabaseFileProvider database;\n        private readonly TaskSchedulerSelector taskSchedulerSelector;\n\n        private int effectCompileCount;\n\n        public bool CompileEffectAsynchronously { get; set; }\n\n        /// <summary>\n        /// If we have to compile a new shader, what kind of cache are we building?\n        /// </summary>\n        public EffectBytecodeCacheLoadSource CurrentCache { get; set; } = EffectBytecodeCacheLoadSource.DynamicCache;\n\n        public EffectCompilerCache(EffectCompilerBase compiler, DatabaseFileProvider database, TaskSchedulerSelector taskSchedulerSelector = null) : base(compiler)\n        {\n            CompileEffectAsynchronously = true;\n            this.database = database ?? throw new ArgumentNullException(nameof(database), \"Using the cache requires a database.\");\n            this.taskSchedulerSelector = taskSchedulerSelector;\n        }\n\n        public override void ResetCache(HashSet<string> modifiedShaders)\n        {\n            // remove old shaders from cache\n            lock (bytecodes)\n            {\n                base.ResetCache(modifiedShaders);\n                RemoveObsoleteStoredResults(modifiedShaders);\n            }\n\n            // A compiled result is memoized per effect input hash, which doesn't change when a shader\n            // file does. Left in place, the first reload would be the only one ever to take effect.\n            lock (compilingShaders)\n            {\n                foreach (var key in compiledShaders\n                    .Where(x => x.Value.Bytecode != null && IsBytecodeObsolete(x.Value.Bytecode, modifiedShaders))","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Effects/Compiler/EffectCompilerCache.cs#L38-L74","documentation":"The EffectCompilerCache constructor wraps an underlying compiler with a database-backed bytecode cache. A DatabaseFileProvider is mandatory — without it there is nowhere to read/write cached bytecode — so a null database produces this ArgumentNullException.","triggerScenarios":"new EffectCompilerCache(compiler, null) — passing a null DatabaseFileProvider, e.g. when the file provider/database was not initialized or a factory returned null.","commonSituations":"Bootstrapping a game/service before the database file provider is created; DI containers failing to resolve the database; skipping database setup on headless build agents.","solutions":["Create and pass a DatabaseFileProvider backed by your effect/obj virtual file system before constructing the cache.","Verify the database initialization code runs before the compiler chain is built.","If you truly have no database, use the underlying compiler directly instead of EffectCompilerCache."],"exampleFix":"// before\nvar cache = new EffectCompilerCache(compiler, null, schedulerSelector);\n// after\nvar database = new DatabaseFileProvider(VirtualFileSystemMountResult);\nvar cache = new EffectCompilerCache(compiler, database, schedulerSelector);","handlingStrategy":"validation","validationCode":"if (database == null) throw new InvalidOperationException(\"Initialize the DatabaseFileProvider before constructing EffectCompilerCache\");","typeGuard":"null","tryCatchPattern":"try { var cache = new EffectCompilerCache(compiler, database); } catch (ArgumentNullException ex) when (ex.ParamName == \"database\") { /* initialize database and retry */ }","preventionTips":["Initialize the database file provider during bootstrap, before compiler chain setup","Verify DI registrations for DatabaseFileProvider","Log database initialization order in startup diagnostics"],"tags":["null-argument","cache","configuration"],"backgroundTag":"null-argument","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}