stride3d/stride · error · InvalidOperationException
OpCode not supported when compiling constant
Error message
OpCode {i.Op} not supported when compiling constant {expression} What it means
CompileConstantValue builds a SPIR-V constant for a parsed shader expression. It consumes instructions from a scratch buffer and expects each to be a constant-defining opcode; if the instruction has any other opcode, the compiler cannot represent the expression as a constant and throws InvalidOperationException.
Solutions
- Check the expression used in the constant context is genuinely compile-time constant (literals, constant composites)
- If the value depends on runtime input, move it to a variable instead of a constant context
- Use supported constant expressions only (e.g. simple arithmetic on constants) or extend CompileConstantValue to handle the missing opcode
- Inspect the failing opcode name in the message to identify which SPIR-V instruction is unsupported
Example fix
// before const int N = ComputeSize(); // function call in constant context // after const int N = 16; // literal constant
Defensive patterns
Strategy: validation
Validate before calling
bool IsCompileTimeConstant(SpirvInstruction i) => i.Op is OpCode.OpConstant or OpCode.OpConstantTrue or OpCode.OpConstantFalse or OpCode.OpConstantNull or OpCode.OpConstantComposite or OpCode.OpUndef;
if (!IsCompileTimeConstant(instruction)) throw new NotSupportedException($"Non-constant opcode {instruction.Op} in constant context"); Type guard
static bool IsConstantOp(OpData i) => i.Op is OpCode.OpConstant or OpCode.OpConstantComposite;
Prevention
- Only pass literal/composite expressions into constant contexts (array sizes, offsets)
- Validate shader source constants in SDSL before SPIR-V emission
- Log the offending opcode early in custom emitter code
When it happens
Trigger: Calling CompileConstantValue on an expression whose compiled instruction stream contains a non-constant opcode (e.g. an instruction producing a result id that is not OpConstant/OpConstantComposite-family), typically after an expression that requires runtime evaluation was fed into a constant context.
Common situations: Using non-constant expressions (function calls, runtime math, stream reads) where SDSL expects a compile-time constant: array sizes, layout offsets, or constant initializers.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Could not load shader
- Unsupported execution model
- Struct not found in shader
- Unsupported base type
- Unsupported type for non-pointer indexer
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/147cb778231740c4.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/ExpressionExtensions.cs:149
var resultId = i.Data.Memory.Span[2];
// Try to fold: if all operands are known constants, compute the result at compile time.
if (TryFoldConstantOp(context, i, out var foldedInstruction))
{
context.Add(foldedInstruction);
result = new(resultId, resultType);
}
else
{
Span<int> instruction = [(int)Op.OpSpecConstantOp, resultType, resultId, (int)i.Op, .. i.Data.Memory.Span[3..]];
instruction[0] |= instruction.Length << 16;
context.Add(new OpData(instruction));
result = new(resultId, resultType);
}
}
else
{
throw new InvalidOperationException($"OpCode {i.Op} not supported when compiling constant {expression}");
}
}
buffer.Dispose();
return result;
}
/// <summary>
/// Returns true if the given ID refers to an OpConstant, OpConstantTrue, OpConstantFalse,
/// or OpConstantComposite (i.e. not a spec constant).
/// </summary>
private static bool IsPlainConstant(SpirvContext context, int id)
{
if (!context.GetBuffer().TryGetInstructionById(id, out var inst))
return false;
return inst.Op is Op.OpConstant or Op.OpConstantTrue or Op.OpConstantFalse or Op.OpConstantComposite or Op.OpConstantNull;
}View on GitHub (pinned to 96fad776d2)