stride3d/stride · error · NotSupportedException
Type ' ' is not supported as a SymbolType.
Error message
Type '{typeof(T)}' is not supported as a SymbolType. What it means
SymbolType.Of<T>() maps a small fixed set of .NET primitive types (scalars, System.Numerics vectors and matrices) to their shader SymbolType equivalents. If T is any other type, the switch's default arm throws NotSupportedException. It is an internal whitelist lookup, not a general converter.
Solutions
- Use a supported T: one of void, bool, byte, sbyte, short, ushort, Half, int, uint, float, double, System.Numerics.Vector2/3/4, Matrix3x2, Matrix4x4.
- If you need a custom type, construct the SymbolType directly (ScalarType, VectorType.From, MatrixType.From) instead of calling Of<T>.
- If the type should legitimately be supported, add a mapping arm to SymbolType.Of<T> in SymbolTypes.cs.
- Guard with a whitelist check before calling to fail fast with a clearer message.
Example fix
// before var t = SymbolType.Of<long>(); // NotSupportedException // after var t = ScalarType.Int64; // 'long' maps to Scalar.Int64
Defensive patterns
Strategy: validation
Validate before calling
static readonly HashSet<Type> Supported = new() {
typeof(void), typeof(bool), typeof(byte), typeof(sbyte),
typeof(short), typeof(ushort), typeof(Half), typeof(int),
typeof(uint), typeof(float), typeof(double),
typeof(System.Numerics.Vector2), typeof(System.Numerics.Vector3),
typeof(System.Numerics.Vector4), typeof(System.Numerics.Matrix3x2),
typeof(System.Numerics.Matrix4x4) };
if (!Supported.Contains(typeof(T)))
throw new ArgumentException($"{typeof(T)} has no SymbolType mapping");
var t = SymbolType.Of<T>(); Type guard
static bool HasSymbolType<T>() =>
typeof(T) == typeof(void) || typeof(T) == typeof(bool) ||
typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte) ||
typeof(T) == typeof(short) || typeof(T) == typeof(ushort) ||
typeof(T) == typeof(Half) || typeof(T) == typeof(int) ||
typeof(T) == typeof(uint) || typeof(T) == typeof(float) ||
typeof(T) == typeof(double) ||
typeof(System.Numerics.Vector2).IsAssignableFrom(typeof(T)) ||
typeof(System.Numerics.Vector3).IsAssignableFrom(typeof(T)) ||
typeof(System.Numerics.Vector4).IsAssignableFrom(typeof(T)) ||
typeof(System.Numerics.Matrix3x2).IsAssignableFrom(typeof(T)) ||
typeof(System.Numerics.Matrix4x4).IsAssignableFrom(typeof(T)); Try / catch
try { var t = SymbolType.Of<T>(); }
catch (NotSupportedException ex)
{
// fall back to manual SymbolType construction or report unsupported T
} Prevention
- Restrict generic parameters to the documented whitelist of types via a marker interface or where-constraint comments.
- Never assume numeric equivalence (long, decimal, nint) is supported.
- Use exact System.Numerics types; custom vector structs will not match typeof comparisons.
- Unit-test generic binding code for every T you use.
When it happens
Trigger: Calling SymbolType.Of<T>() with a generic argument outside the supported set: e.g. Of<long>(), Of<Half> on platforms where it's unmapped, Of<decimal>(), Of<Vector2> from a different namespace, custom structs, or reference types like string.
Common situations: Developers writing generic shader-parameter binding code assume Of<T> works for any numeric type (e.g. long, decimal); refactoring a uniform struct to a type not on the whitelist; using System.Numerics analogues or user-defined vector types that are not the exact typeof(Vector3) etc.
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
- paramKey
- key
- Generic Parameters are not supported with
- Type [ ] is not supported as a modifiable collection
- The type doesn't have an assembly-qualified name
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/4f362c52ee28e0a2.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Core/SymbolTypes.cs:162
var t when t == typeof(bool) => ScalarType.From("bool"),
var t when t == typeof(byte) => ScalarType.From("byte"),
var t when t == typeof(sbyte) => ScalarType.From("sbyte"),
var t when t == typeof(short) => ScalarType.From("short"),
var t when t == typeof(ushort) => ScalarType.From("ushort"),
var t when t == typeof(Half) => ScalarType.From("half"),
var t when t == typeof(int) => ScalarType.From("int"),
var t when t == typeof(uint) => ScalarType.From("uint"),
var t when t == typeof(float) => ScalarType.From("float"),
var t when t == typeof(double) => ScalarType.From("double"),
var t when t == typeof(System.Numerics.Vector2) => VectorType.From("float2"),
var t when t == typeof(System.Numerics.Vector3) => VectorType.From("float3"),
var t when t == typeof(System.Numerics.Vector4) => VectorType.From("float4"),
var t when t == typeof(System.Numerics.Matrix3x2) => MatrixType.From("float3x2"),
var t when t == typeof(System.Numerics.Matrix4x4) => MatrixType.From("float4x4"),
_ => throw new NotSupportedException($"Type '{typeof(T)}' is not supported as a SymbolType."),
};
}
}
public sealed partial record UndefinedType(string TypeName) : SymbolType()
{
public override string ToString()
{
return TypeName;
}
}
public sealed partial record PointerType(SymbolType BaseType, Specification.StorageClass StorageClass) : SymbolType()
{
public override string ToId() => $"ptr_{StorageClass}_{BaseType.ToId()}";
public override string ToString() => $"*{BaseType}";
}
View on GitHub (pinned to 96fad776d2)