stride3d/stride · error · InvalidOperationException

Generic argument ( ) for could not be resolved during mix…

Error message

Generic argument {i} ({expr}) for {classSource.ClassName} could not be resolved during mix phase

What it means

When instantiating a generic shader during the mix phase, each ConstantExpression in classSource.GenericArguments must TryEvaluate to a non-null constant so it can be converted to a string via ShaderClassSource.ConvertGenericArgToString. If any argument cannot be evaluated to a constant, the builder throws InvalidOperationException naming the argument index, expression, and class.

Solutions

  1. Provide concrete constant expressions (literals or previously resolved constants) for every generic argument.
  2. Import the shader whose generic parameter the expression references so it can be substituted before the mix phase.
  3. Evaluate the argument earlier in the pipeline (ResolveStep.Compile) and store the resolved value in classSource.GenericArguments.

Example fix

// before
var source = new ShaderClassSource("MyShader", new GenericParamExpr("Parent", 0));
// after
var source = new ShaderClassSource("MyShader", new LiteralExpr(4.0f));
Defensive patterns

Strategy: validation

Validate before calling

for (int i = 0; i < classSource.GenericArguments.Length; i++)
    if (!classSource.GenericArguments[i].TryEvaluate(out var v) || v is null)
        throw new ArgumentException($"Generic argument {i} of {classSource.ClassName} must be a compile-time constant");

Type guard

bool IsConcrete(ConstantExpression e) => e.TryEvaluate(out var v) && v is not null;

Try / catch

try { var result = builder.InstantiateGenericShader(loader, classSource, macros); }
catch (InvalidOperationException ex) when (ex.Message.Contains("could not be resolved during mix phase")) { /* substitute generics earlier or supply literals */ }

Prevention

When it happens

Trigger: Calling GetOrLoadShader / InstantiateGenericShader with a ShaderClassSource whose GenericArguments still contain unresolved GenericParamExpr or symbolic expressions at mix phase.

Common situations: Instantiating `MyShader<Parent.N>` where Parent's generic N was never substituted because Parent wasn't imported; passing an expression referencing a runtime value instead of a compile-time constant; nested generic instantiations where an inner argument stayed symbolic.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Class.cs:814

    /// <param name="classSource">The generics parameters should be in <see cref="parentBuffer"/>.</param>
    /// <param name="macros"></param>
    /// <param name="resolveStep"></param>
    /// <returns></returns>
    /// <param name="parentBuffer"></param>
    public static ShaderBuffers GetOrLoadShader(IExternalShaderLoader shaderLoader, ShaderClassInstantiation classSource, ReadOnlySpan<ShaderMacro> macros, ResolveStep resolveStep, SpirvContext context)
    {
        if (resolveStep == ResolveStep.Mix && classSource.GenericArguments.Length > 0)
        {
            // At mix time, generics are fully resolved — resolve to string values
            // and use the value-based path which caches to shaderLoader.Cache (persistent)
            var genericValues = new string[classSource.GenericArguments.Length];
            for (int i = 0; i < genericValues.Length; i++)
            {
                var expr = classSource.GenericArguments[i];
                if (expr.TryEvaluate(out var constantValue) && constantValue is not null)
                    genericValues[i] = ShaderClassSource.ConvertGenericArgToString(constantValue);
                else
                    throw new InvalidOperationException($"Generic argument {i} ({expr}) for {classSource.ClassName} could not be resolved during mix phase");
            }

            var result = GetOrLoadShader(shaderLoader, classSource.ClassName, genericValues, macros);

            // PostProcess: update classSource (same as GenericResolverFromInstantiatingBuffer.PostProcess)
            var classNameWithGenerics = $"{classSource.ClassName}<{string.Join(",", genericValues)}>";
            classSource.ClassName = classNameWithGenerics;
            classSource.GenericArguments = [];

            return result;
        }

        return GetOrLoadShader(shaderLoader, classSource.ClassName, new GenericResolverFromInstantiatingBuffer(classSource, resolveStep, context), macros);
    }

    public static ShaderBuffers GetOrLoadShader(IExternalShaderLoader shaderLoader, string className, string[]? genericValues, ReadOnlySpan<ShaderMacro> macros)
    {
        if (genericValues is null)

View on GitHub (pinned to 96fad776d2)