stride3d/stride · error · NotSupportedException
Unsupported ternary operand types: condition=
Error message
Unsupported ternary operand types: condition={Condition.ValueType}, left={Left.ValueType}, right={Right.ValueType} What it means
Type inference for the ternary (?:) operator only supports a fixed set of operand combinations: vector condition, scalar/scalar, scalar+vector mixes, and vector/vector pairs, all requiring a valid scalar element type. Any combination outside these patterns — e.g. matrix or struct operands, or a non-scalar element type — throws this NotSupportedException at Expression.cs:1630.
Solutions
- Ensure the condition is a scalar or vector bool and both branches are scalars or vectors of compatible element types.
- For matrices/structs, replace ?: with an explicit if/else or per-component ternary on vectors.
- If a new valid combination should be supported, add a pattern arm to the switch at Expression.cs:1630.
Example fix
// before float3x3 m = b ? m1 : m2; // matrix operands // after float3x3 m; if (b) m = m1; else m = m2;
Defensive patterns
Strategy: validation
Validate before calling
// check ternary operand kinds before compiling
bool ok = cond is ScalarType or VectorType && l is (ScalarType or VectorType) && r is (ScalarType or VectorType);
if (!ok) throw new ShaderCompileHint("Ternary requires scalar/vector condition and branches"); Type guard
static bool IsTernaryCompatible(BaseType c, BaseType l, BaseType r) => c is ScalarType or VectorType && l is ScalarType or VectorType && r is ScalarType or VectorType;
Try / catch
try { result = CompileExpression(expr, table, compiler); }
catch (NotSupportedException ex) when (ex.Message.Contains("Unsupported ternary operand types"))
{ diagnostics.Report(expr.Info, ex.Message); } Prevention
- Keep ternary conditions scalar or vector bool.
- Use if/else instead of ?: for matrices and structs.
- Test type inference for every operand combination you rely on.
When it happens
Trigger: Evaluating `cond ? a : b` where the condition is not scalar/vector or where Left/Right are matrices, structs, samplers, or other non-vector/scalar types; also when scalarType inference (the element type of the branches) fails.
Common situations: Selecting between two matrices with ?:, applying ?: to struct/sampler operands, or writing `matrixA ? m1 : m2` expecting component-wise selection like HLSL allows.
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
- Assign back to matrix swizzle is not implemented yet
- BorderColor must be float4(r, g, b, a)
- Can't find a base method for
- Can't find function in current mixin
- Can't find method group info for
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/862fa496f58cf489.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs:1630
{
Condition.ProcessSymbol(table);
if (Condition.ValueType!.GetElementType() is not ScalarType)
table.AddError(new(Condition.Info, SDSLErrorMessages.SDSL0106));
Left.ProcessSymbol(table);
Right.ProcessSymbol(table);
var scalarType = SpirvBuilder.FindCommonBaseTypeForBinaryOperation(Left.ValueType!.GetElementType(), Right.ValueType!.GetElementType());
Type = (Condition.ValueType, Left.ValueType, Right.ValueType) switch
{
// If condition is a vector, we need to use this vector size instead
(VectorType c, _, _) => new VectorType(scalarType, c.Size),
(ScalarType c, ScalarType, ScalarType) => scalarType,
(ScalarType c, VectorType v1, ScalarType s2) => v1.WithElementType(scalarType),
(ScalarType c, ScalarType s1, VectorType v2) => v2.WithElementType(scalarType),
(ScalarType c, VectorType v1, VectorType v2) => new VectorType(scalarType, Math.Min(v1.Size, v2.Size)),
_ => throw new NotSupportedException($"Unsupported ternary operand types: condition={Condition.ValueType}, left={Left.ValueType}, right={Right.ValueType}"),
};
}
public override SpirvValue CompileImpl(SymbolTable table, CompilerUnit compiler)
{
var (builder, context) = compiler;
var conditionValue = Condition.CompileAsValue(table, compiler);
// Might need implicit conversion from float/int to bool, preserving vector shape
if (Condition.ValueType is VectorType condVec)
conditionValue = builder.Convert(context, conditionValue, new VectorType(ScalarType.Boolean, condVec.Size));
else
conditionValue = builder.Convert(context, conditionValue, ScalarType.Boolean);
// TODO: Review choice between if/else like branch (OpBranchConditional) which evaluate only one side, or select (OpSelect) which evaluate both side but can work per component but is limited to specific types
// It seems HLSL 2021 changed the behavior to align it with C-style short-circuiting.
// For now, we use OpSelect only with per-component, otherwise we use if/else branchingView on GitHub (pinned to 96fad776d2)