{"record":{"id":"42f194e9a0fe56c6","repo":"stride3d/stride","slug":"shader-name-was-loaded-without-a-source-hash-missing","errorCode":null,"errorMessage":"Shader '{name}' was loaded without a source hash (missing OpSourceHashSDSL).","messagePattern":"Shader '(.+?)' was loaded without a source hash \\(missing OpSourceHashSDSL\\)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs","lineNumber":1397,"sourceCode":"    /// </summary>\n    /// <remarks>Expects hash to be stored.</remarks>\n    public IShaderCache Cache => inner.Cache;\n    public GenericShaderCache GenericCache => inner.GenericCache;\n\n    public HashSourceCollection Sources { get; } = new();\n\n    public bool Exists(string name) => inner.Exists(name);\n\n    public bool LoadExternalFileContent(string name, out string filename, out string code, out ObjectId hash)\n        => inner.LoadExternalFileContent(name, out filename, out code, out hash);\n\n    public bool LoadExternalBuffer(string name, ReadOnlySpan<ShaderMacro> defines, out ShaderBuffers bytecode, out ObjectId hash, out bool isFromCache)\n    {\n        var result = inner.LoadExternalBuffer(name, defines, out bytecode, out hash, out isFromCache);\n        // A file-backed shader always carries a source hash. A zero here means it was compiled or cached\n        // without its OpSourceHashSDSL, which would make its effect obsolete (recompiled) on every run.\n        if (hash == ObjectId.Empty)\n            throw new InvalidOperationException($\"Shader '{name}' was loaded without a source hash (missing OpSourceHashSDSL).\");\n        if (!Sources.ContainsKey(name))\n            Sources.Add(name, hash);\n        return result;\n    }\n\n    public bool LoadExternalBuffer(string name, string? filename, string code, ReadOnlySpan<ShaderMacro> defines, out ShaderBuffers bytecode, out ObjectId hash, out bool isFromCache)\n        => inner.LoadExternalBuffer(name, filename, code, defines, out bytecode, out hash, out isFromCache);\n\n    public bool SuppressSourceHash { get => inner.SuppressSourceHash; set => inner.SuppressSourceHash = value; }\n}\n","sourceCodeStart":1379,"sourceCodeEnd":1408,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs#L1379-L1408","documentation":"LoadExternalBuffer delegates to the inner loader and then asserts that a file-backed shader always carries a non-empty source hash. A zero ObjectId means the shader was compiled or cached without its OpSourceHashSDSL instruction, which would make the effect appear stale and be recompiled on every run. The mixer treats that as an invalid input buffer and throws InvalidOperationException.","triggerScenarios":"Calling ShaderMixer.LoadExternalBuffer(name, defines, ...) when the SPIR-V/SDSL buffer on disk or in cache was produced with source-hash emission disabled (emitSourceHash=false) or by an older compiler that did not emit OpSourceHashSDSL, so the returned hash is ObjectId.Empty.","commonSituations":"Mixing shader binaries produced by a different compiler version; cache entries written before OpSourceHashSDSL emission was introduced; a build step that compiles shaders with hashing turned off for speed.","solutions":["Recompile the shader (delete the stale cache entry and regenerate it) with source-hash emission enabled so OpSourceHashSDSL is embedded.","Check the code path that writes the external buffer and ensure emitSourceHash: true is passed to the compiler.","If you control the loader, verify the buffer's hash on disk (or with an offline tool) before handing it to LoadExternalBuffer."],"exampleFix":"// before\nsdslc.Compile(filename, text, hash, macros, log, out buffer, new() { EmitSourceHash = false });\n// after\nsdslc.Compile(filename, text, hash, macros, log, out buffer, new() { EmitSourceHash = true });","handlingStrategy":"validation","validationCode":"// check the buffer's hash before handing it to the mixer\nvar loader = ...;\nloader.LoadExternalBuffer(name, defines, out var bytecode, out var hash, out _);\n// hash was checked internally; pre-validate cache files instead:\nbool HasSourceHash(ObjectId hash) => hash != ObjectId.Empty;","typeGuard":null,"tryCatchPattern":"try { mixer.LoadExternalBuffer(name, defines, out var bc, out var hash, out var cached); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"OpSourceHashSDSL\")) {\n    RecompileShader(name, defines); // regenerate with EmitSourceHash = true\n}","preventionTips":["Always compile/cached-emit shaders with EmitSourceHash = true.","Purge shader caches when upgrading the compiler version.","Spot-check cached buffers for OpSourceHashSDSL after build pipeline changes."],"tags":["shader-cache","missing-metadata","spirv"],"backgroundTag":"checksum-mismatch","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"}