stride3d/stride · error · InvalidOperationException
Operator ' ' is not supported on matrix types
Error message
Operator '{op}' is not supported on matrix types What it means
When a binary operator is applied to matrix operands in the SPIR-V builder, only +, -, *, and / are supported; the matrix is decomposed into column vectors and the op applied per column. Any other operator (e.g. %, bitwise, shift) on a MatrixType is rejected because SPIR-V arithmetic instructions only accept scalar/vector operands and there is no per-column lowering defined for that operator.
Solutions
- Replace the unsupported operator with one of +, -, *, or / on the matrices
- If you need a component-wise non-arithmetic op, decompose the matrix into column vectors yourself (OpCompositeExtract), apply the op per column, and recompose (OpCompositeConstruct)
- Use a library helper or explicit loop over matrix elements instead of a direct matrix operator
- Check the operator parsing in the shader source; a typo may be producing an unexpected Operator enum value
Example fix
// before float4x4 r = a % b; // Operator 'Modulus' is not supported on matrix types // after float4x4 r = a / b; // or implement per-component: // for (int i = 0; i < 4; i++) r[i] = a[i] % b[i];
Defensive patterns
Strategy: validation
Validate before calling
if (left is MatrixType && !new[]{Operator.Plus,Operator.Minus,Operator.Mul,Operator.Div}.Contains(op))
throw new ArgumentException($"{op} is not supported on matrices; use +,-,*,/"); Type guard
bool IsMatrixSupportedOp(SymbolType t, Operator op) => t is not MatrixType || op is Operator.Plus or Operator.Minus or Operator.Mul or Operator.Div;
Try / catch
try { result = Builder.BinaryOperation(op, left, right); }
catch (InvalidOperationException ex) when (ex.Message.Contains("not supported on matrix types"))
{ /* decompose into columns or rewrite the expression */ } Prevention
- Only apply +,-,*,/ directly to matrices in shader code
- Rewrite modulus/bitwise/shift matrix math as per-component loops
- Add unit tests covering each operator on matrix operands
When it happens
Trigger: Using an operator other than Plus/Minus/Mul/Div between two matrix-typed values in shader code compiled through BinaryOperation (e.g. matrices % matrices, matrix << matrix, or matrix & matrix).
Common situations: Porting HLSL shader code that uses non-arithmetic matrix operators; writing custom stream/patch logic via PatchStreamsAccesses that combines matrices with unsupported operators; typos where a scalar modulus was intended but both operands were matrices.
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
- Binary operator not supported for element type
- Can't cast between array of different sizes
- Can't cast between array of different types
- Can't cast between structures
- Can't cast from to (larger matrix)
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/3e69d7e7da46ca07.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:300
(var operandType, var resultType) = AnalyzeBinaryOperation(table, leftType, op, rightType, info) ?? throw new InvalidOperationException("Type of binary operation could not be determined");
// TODO: Some specific cases where one of the operands doesn't need to have exact same type as resultType (such as shift in OpShiftRightLogical, or signedness for some other operations)
// We'll need to review those cases
left = Convert(context, left, operandType);
right = Convert(context, right, operandType);
var resultTypeId = context.GetOrRegister(resultType);
// Refresh types (after convert)
leftType = context.ReverseTypes[left.TypeId];
rightType = context.ReverseTypes[right.TypeId];
// SPIR-V arithmetic ops (OpFAdd, OpFSub, OpFMul, etc.) only accept scalar/vector types.
// For matrices, decompose into column vectors, apply the op per-column, then reconstruct.
if (leftType is MatrixType matType)
{
if (op is not (Operator.Plus or Operator.Minus or Operator.Mul or Operator.Div))
throw new InvalidOperationException($"Operator '{op}' is not supported on matrix types");
var columnTypeId = context.GetOrRegister(new VectorType(matType.BaseType, matType.Rows));
Span<int> columnResults = stackalloc int[matType.Columns];
for (int i = 0; i < matType.Columns; i++)
{
var colLeft = Buffer.Insert(Position++, new OpCompositeExtract(columnTypeId, context.Bound++, left.Id, [i])).ToValue();
var colRight = Buffer.Insert(Position++, new OpCompositeExtract(columnTypeId, context.Bound++, right.Id, [i])).ToValue();
columnResults[i] = BinaryOperation(table, context, colLeft, op, colRight, info).Id;
}
return Buffer.Insert(Position++, new OpCompositeConstruct(resultTypeId, resultId, [.. columnResults])).ToValue();
}
var leftElementType = leftType.GetElementType();
var rightElementType = rightType.GetElementType();
var spirvOp = SelectBinaryOp(op, leftElementType);
// All SPIR-V binary ops share the same 5-word layout (wordcount|opcode, resultType, resultId, operand1, operand2),
// so we create one and patch the opcode.View on GitHub (pinned to 96fad776d2)