stride3d/stride · error · InvalidOperationException

Struct not found in shader

Error message

Struct {importStruct.StructName} not found in shader {shaderName}

What it means

During SDSL shader mixing, an OpImportStructSDSL instruction imports a struct type declared in another shader referenced via the external shaders context. The mixer looks the struct up in the target shader's own StructTypes and, failing that, in its associated Stage's StructTypes. If the struct is declared in neither, the shader composition is invalid and merging aborts.

Solutions

  1. Ensure the struct with the exact name exists in the shader being imported from (or in its Stage's StructTypes).
  2. Fix the struct name in the import statement to match the declaration.
  3. Check that globalContext.ExternalShaders maps importStruct.Shader to the intended shader.
  4. Rebuild/clean shader compilation caches after refactoring shader code.

Example fix

// before (shader B imports a struct that no longer exists)
import Struct ShadowParameters from A;
// after (re-add the struct in shader A or correct the name)
struct ShadowParameters { float bias; }; // in shader A
// or: import Struct ShadowParams from A; // matching A's actual struct name
Defensive patterns

Strategy: validation

Validate before calling

// Before composing, verify the imported struct exists in the source shader
if (!sourceShader.StructTypes.ContainsKey(importedStructName) &&
    (sourceShader.Stage == null || !sourceShader.Stage.StructTypes.ContainsKey(importedStructName)))
    throw new InvalidOperationException($"Struct '{importedStructName}' missing in '{shaderName}' before mixing");

Try / catch

// Wrap shader mixing during content/asset build
class ShaderMixException : Exception
{
    public ShaderMixException(string shader, string structName, Exception inner)
        : base($"Failed to mix '{shader}': missing struct '{structName}'. Check import declarations.", inner) { }
}
try { var result = mixer.Merge(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("not found in shader"))
{ throw new ShaderMixException(shaderName, structName, ex); }

Prevention

When it happens

Trigger: ShaderMixer.MergeClassInBuffers encounters an OpImportStructSDSL whose Shader index resolves (via globalContext.ExternalShaders) to a shader that does not declare a struct named importStruct.StructName in ShaderInfo.StructTypes or its Stage.StructTypes.

Common situations: Renaming or deleting a struct in a base shader while dependent shaders still import it; misspelling the struct name in the import; importing from a shader that only declares the struct in a different mixin/stage not attached here; stale compiled shader classes after refactoring SDSL sources.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/e4a29172c6e3b254. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.cs:616

                // Detect when we switch from context to main buffer
                if (i2.Op == Op.OpShaderSDSL)
                {
                    isContext = false;
                }

                // Specific type instructions in context gets deduplicated before adding
                bool addToContext = false;
                if (TypeDuplicateHelper.OpCheckDuplicateForTypesAndImport(i2.Op))
                {
                    // We need to replace those right now (otherwise further types depending on this struct won't get properly translated)
                    if (i2.Op == Op.OpImportStructSDSL && new OpImportStructSDSL(ref i2) is { } importStruct)
                    {
                        var shaderName = globalContext.ExternalShaders[importStruct.Shader];
                        var shader2 = mixinNode.ShadersByName[shaderName];
                        if (!shader2.StructTypes.TryGetValue(importStruct.StructName, out var structId)
                            && (shader2.Stage == null || !shader2.Stage.StructTypes.TryGetValue(importStruct.StructName, out structId)))
                            throw new InvalidOperationException($"Struct {importStruct.StructName} not found in shader {shaderName}");
                        remapIds.Add(importStruct.ResultId, structId);
                        removedIds.Add(structId);
                    }
                    else
                    {
                        // Check if type already exists in context (deduplicate them)
                        if (typeDuplicateInserter.CheckForDuplicates(i2, out var existingInstruction))
                        {
                            if (i2.IdResult is int duplicateId)
                            {
                                remapIds.Add(duplicateId, existingInstruction.Data.IdResult!.Value);
                                var mismatches = typeDuplicateInserter.MergeTypeDecorations(existingInstruction.Data.IdResult.Value, duplicateId);
                                if (mismatches != null)
                                {
                                    var details = string.Join("; ", mismatches.Select(m =>
                                        $"{m.Data} (only on {(m.OnKeepOnly ? "kept" : "removed")} type)"));
                                    globalContext.Log.Warning($"Mismatched decorations when merging type {duplicateId} into {existingInstruction.Data.IdResult.Value}: {details}");
                                    foreach (var entry in mismatches)

View on GitHub (pinned to 96fad776d2)