stride3d/stride · error · InvalidOperationException

Semantic requested with type but last time with

Error message

Semantic {semantic} requested with type {type} but last time with {result.Type}

What it means

EntryPointWrapperGenerator caches declared builtin variables by semantic name. If the same semantic is requested a second time with a different SymbolType than the first declaration, it throws InvalidOperationException because SPIR-V requires one consistent type per builtin variable; silently reusing it with a different type would produce invalid SPIR-V.

Solutions

  1. Make all uses of the same semantic use one identical SymbolType (match component count and base type) in the entry-point signature.
  2. If different types are truly needed, use distinct semantic names so each gets its own builtin variable.
  3. Inspect GetOrDeclareBuiltInValue calls in EntryPointWrapperGenerator.cs:67 to see the conflicting types reported in the message and align the offending declaration.
  4. Recheck upstream SDSL/HLSL struct definitions for the stream layout so types agree before wrapper generation.

Example fix

// before
void CSMain(uint3 id : SV_GroupID, uint3 threadId : SV_GroupThreadID, float3 extra : SV_GroupID) // SV_GroupID as float3 conflicts with uint3

// after
void CSMain(uint3 id : SV_GroupID, uint3 threadId : SV_GroupThreadID) // single consistent uint3 declaration per semantic
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every semantic is declared with exactly one type across the entry-point signature
var bySemantic = parameters.GroupBy(p => p.Semantic, StringComparer.OrdinalIgnoreCase);
bool typesConsistent = bySemantic.All(g => g.Select(p => p.Type).Distinct().Count() == 1);

Type guard

static bool HasConsistentSemanticTypes(IEnumerable<(string Semantic, SymbolType Type)> decls) =>
    decls.GroupBy(d => d.Semantic, StringComparer.OrdinalIgnoreCase)
         .All(g => g.Select(d => d.Type).Distinct().Count() <= 1);

Try / catch

try
{
    var id = GetOrDeclareBuiltInValue(type, semantic);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("requested with type"))
{
    log.Error($"Conflicting types for semantic '{semantic}': {ex.Message}");
    throw new ShaderCompilationException($"Semantic '{semantic}' declared with conflicting types in entry point", ex);
}

Prevention

When it happens

Trigger: GenerateWrapper processes two parameters (or two stream members) carrying the same semantic string, but their resolved SymbolTypes differ, e.g. the same SV_ semantic declared as float4 in one place and float3 (or uint) in another within a single entry-point wrapper.

Common situations: HLSL/SDSL signatures where the same system value appears on both an entry-point parameter and a struct member with mismatched vector/component types; recent refactors that changed one declaration's type but not the other; copy-paste of parameters where one lost a swizzle-compatible type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Processing/Interfaces/Generation/EntryPointWrapperGenerator.cs:67

                var variableValueType = variable.Value.Type.BaseType;
                var methodInitializerCall = buffer.Add(new OpFunctionCall(context.GetOrRegister(variableValueType), context.Bound++, methodInitializerId, []));
                buffer.Add(new OpStore(variable.Value.VariableId, methodInitializerCall.ResultId, null, []));
            }
        }

        // Update entry point type (since Streams type might have been replaced)
        entryPointFunctionType = (FunctionType)entryPoint.Type;

        var builtinVariables = new Dictionary<string, (SymbolType Type, int Id)>();
        var entryPointExtraVariables = new List<int>();

        int GetOrDeclareBuiltInValue(SymbolType type, string semantic)
        {
            semantic = semantic.ToUpperInvariant();
            if (builtinVariables.TryGetValue(semantic, out var result))
            {
                if (result.Type != type)
                    throw new InvalidOperationException($"Semantic {semantic} requested with type {type} but last time with {result.Type}");
                return result.Id;
            }

            // Declare the global builtin
            var variableId = context.Bound++;
            if (!BuiltinProcessor.ProcessBuiltinsDecoration(context, executionModel, variableId, StreamVariableType.Input, semantic, ref type))
                throw new InvalidOperationException();
            var variable = context.Add(new OpVariable(context.GetOrRegister(new PointerType(type, Specification.StorageClass.Input)), variableId, Specification.StorageClass.Input, null)).ResultId;
            entryPointExtraVariables.Add(variable);
            var value = buffer.Add(new OpLoad(context.GetOrRegister(type), context.Bound++, variable, null, [])).ResultId;
            builtinVariables.Add(semantic, (type, value));
            return value;
        }

        void FillSemanticArguments(FunctionType functionType, Span<int> arguments)
        {
            foreach (var i in context)
            {

View on GitHub (pinned to 96fad776d2)