stride3d/stride · error · NotSupportedException
Unsupported template type
Error message
Unsupported template type {templateType} for Buffer What it means
SymbolTypes.TryGetBufferType resolves HLSL buffer declarations (Buffer<T>, RWBuffer<T>) to SymbolTypes. Typed buffers may only be templated on a scalar or vector element type; after widening half→float (per VUID-StandaloneSpirv-OpTypeImage-04656), any other template type throws NotSupportedException('Unsupported template type ... for Buffer').
Solutions
- Use StructuredBuffer<T> / RWStructuredBuffer<T> instead of Buffer<T> for struct, matrix, or other composite element types.
- Keep the typed buffer's template restricted to scalar (float, int, uint) or vector (float2/3/4, int4, ...) types.
- If half-based elements are intended, note they are widened to float; use StructuredBuffer<min16float> semantics if half precision must be preserved.
- If a scalar/vector template type still triggers this, inspect what the template TypeName resolved to — an unresolved alias may be mapping to a non-vector SymbolType.
Example fix
// before Buffer<MyStruct> data; // NotSupportedException // after StructuredBuffer<MyStruct> data;
Defensive patterns
Strategy: type-guard
Validate before calling
static bool IsValidBufferTemplate(SymbolType? t) =>
t is null or VectorType or ScalarType; // scalar/vector (half is auto-widened); null defaults to float4
if (!IsValidBufferTemplate(templateTypeName?.Type))
throw new NotSupportedException("Typed Buffer<T> requires a scalar or vector T; use StructuredBuffer<T> otherwise"); Type guard
bool IsScalarOrVector(SymbolType t) => t is VectorType or ScalarType;
Try / catch
try
{
SymbolTypes.TryGetBufferType(name, templateTypeName, out var sym);
}
catch (NotSupportedException ex) when (ex.Message.Contains("Unsupported template type"))
{
logger.LogError("Buffer '{Name}' templated on a composite type; rewrite as StructuredBuffer", name);
throw;
} Prevention
- Reserve Buffer/RWBuffer for scalar and vector element types; use StructuredBuffer for structs and matrices.
- Add a shader-authoring lint that flags Buffer<T> where T is not a scalar/vector.
- Remember half templates are widened to float — don't rely on Buffer<half> keeping 16-bit precision.
- Cover typical buffer templates (float, float4, int, uint, and one struct case) in parser tests.
When it happens
Trigger: Declaring/parsing 'Buffer<T>' or 'RWBuffer<T>' where T resolves to a non-scalar/non-vector type — e.g. Buffer<MyStruct>, Buffer<float4x4>, Buffer<SomeMatrix>, or a custom UDT. (StructuredBuffer/RWStructuredBuffer/Append/ConsumeStructuredBuffer are exempt and accept arbitrary types.)
Common situations: Porting HLSL code that (illegally per FXC rules too, or permissively accepted elsewhere) templates a typed buffer with a struct or matrix; authoring new shader code assuming Buffer behaves like StructuredBuffer; a preceding parse failure leaving templateTypeName resolved to a composite type.
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
- foreach: Array type is expected
- foreach: collection and variable type not matching
- Unsupported literal type
- Unsupported literal type
- A NodeIndex instance cannot be passed as the value of…
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/8c0402bfe64ecf99.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Core/SymbolTypes.cs:81
case "ConsumeStructuredBuffer":
result = new ConsumeStructuredBufferType(templateTypeName!.Type!);
return true;
}
// Note: templateTypeName is resolved lazily (because it might not be a buffer type and we don't need to resolve it)
// Preserves the full vector/scalar type (e.g. float2 stays float2). Defaults to float4 when no template given (HLSL default).
static SymbolType ResolveReturnType(TypeName? templateTypeName)
=> WidenImageElementType(templateTypeName == null ? new VectorType(ScalarType.Float, 4) : templateTypeName.Type!);
// Typed buffers only allow scalar/vector element types.
static SymbolType ResolveBufferReturnType(TypeName? templateTypeName)
{
var templateType = WidenImageElementType(templateTypeName?.Type ?? new VectorType(ScalarType.Float, 4));
return templateType switch
{
VectorType or ScalarType => templateType,
_ => throw new NotSupportedException($"Unsupported template type {templateType} for Buffer"),
};
}
// Vulkan requires an image's sampled type to be 32-bit int, 64-bit int or 32-bit float
// (VUID-StandaloneSpirv-OpTypeImage-04656). The 16 bits live in the pixel format, so
// widening the element type loses nothing.
static SymbolType WidenImageElementType(SymbolType elementType) => elementType switch
{
ScalarType { Type: Scalar.Half } => ScalarType.Float,
VectorType { BaseType: ScalarType { Type: Scalar.Half }, Size: var size } => new VectorType(ScalarType.Float, size),
_ => elementType,
};
SymbolType? foundType = name switch
{
"Buffer" => new BufferType(ResolveBufferReturnType(templateTypeName)),
"RWBuffer" => new BufferType(ResolveBufferReturnType(templateTypeName), true),
View on GitHub (pinned to 96fad776d2)