stride3d/stride · error · InvalidOperationException

External variable not found

Error message

External variable {variable.Name} not found

What it means

While processing a member access to an external variable, the mixer resolves the variable in the current mixin's shader info. If not found there, it retries as a stage variable via instanceMixinGroup.Stage.ShadersByName and the stage shader's Variables. When both lookups fail, the referenced external variable does not exist in any resolvable scope.

Solutions

  1. Declare the variable (e.g. as extern or stage variable) in the shader that owns it, and ensure that shader is attached/composed.
  2. Correct the variable name spelling at the access site.
  3. Verify instanceMixinGroup.Stage is set when the variable is a stage variable.
  4. Check that the variable is registered in the shader's Variables table (correct stream/type section).

Example fix

// before (variable not declared where accessed)
float4 color = BaseColor;
// after
extern float4 BaseColor; // declared in the referenced shader, or access it via the composition path
Defensive patterns

Strategy: validation

Validate before calling

// Verify the external variable is declared in the mixin or stage before mixing
bool exists = shaderInfo.Variables.ContainsKey(varName)
    || (instanceMixinGroup.Stage != null
        && instanceMixinGroup.Stage.ShadersByName.TryGetValue(shaderName, out var si)
        && si.Variables.ContainsKey(varName));
if (!exists) throw new InvalidOperationException($"Variable '{varName}' must be declared extern or as a stage variable");

Try / catch

try { var mixed = mixer.Process(...); }
catch (InvalidOperationException ex) when (ex.Message.Contains("External variable") && ex.Message.Contains("not found"))
{
    log.Error(ex, "External variable unresolved; declare it extern/stage in the owning shader");
    throw;
}

Prevention

When it happens

Trigger: ShaderMixer constructor processes an OpMemberAccess whose member is an external variable; the variable name is absent from the mixin's Variables and, when Stage is non-null, from the stage shader's Variables (or Stage is null and shaderName lookup failed).

Common situations: Referencing a variable from another shader without the proper extern/attach declaration; typo in the variable name; the variable exists only in a base shader that is not attached as a stage; refactoring removed the variable while callers remain.

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/c3eb4cefc232f83b. Report an issue: GitHub.

Appendix: source

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

                {
                    if (!compositionArrayAccesses.TryGetValue(memberAccess.Instance, out instanceMixinGroup!)
                        && !mixinNode.Compositions.TryGetValue(memberAccess.Instance, out instanceMixinGroup!))
                        throw new InvalidOperationException();
                }

                if (globalContext.ExternalVariables.TryGetValue(memberAccess.Member, out var variable))
                {
                    var shaderName = globalContext.ExternalShaders[variable.ShaderId];

                    var shaderInfo = instanceMixinGroup.ShadersByName[shaderName];
                    if (!shaderInfo.Variables.TryGetValue(variable.Name, out var variableInfo))
                    {
                        // Try as a stage variable
                        if (!(instanceMixinGroup.Stage != null
                            && instanceMixinGroup.Stage.ShadersByName.TryGetValue(shaderName, out shaderInfo)
                            && shaderInfo.Variables.TryGetValue(variable.Name, out variableInfo)))
                        {
                            throw new InvalidOperationException($"External variable {variable.Name} not found");
                        }
                    }
                    memberAccesses.Add(memberAccess.ResultId, variableInfo.Id);
                }
                else if (context.ReverseTypes[memberAccess.ResultType] is FunctionType functionType)
                {
                    // In case of functions, OpMemberAccessSDSL.Member could either be a OpFunction or a OpImportFunctionSDSL
                    var functionId = memberAccess.Member;
                    if (globalContext.ExternalFunctions.TryGetValue(memberAccess.Member, out var function))
                    {
                        // Process member call (composition)
                        if (!instanceMixinGroup.MethodGroupsByName.TryGetValue((function.Name, functionType), out functionId)
                            && (instanceMixinGroup.Stage == null || !instanceMixinGroup.Stage.MethodGroupsByName.TryGetValue((function.Name, functionType), out functionId)))
                            throw new InvalidOperationException($"Can't find function {function.Name} in current mixin");
                    }

                    bool foundInStage = false;
                    if (!instanceMixinGroup.MethodGroups.TryGetValue(functionId, out var methodGroupEntry))

View on GitHub (pinned to 96fad776d2)