stride3d/stride · error · InvalidOperationException

[Color] attribute can only be applied on float3/float4…

Error message

[Color] attribute can only be applied on float3/float4 vector types

What it means

The SDSL [Color] attribute marks a cbuffer member as an editor color picker, which only makes sense for float3/float4 vectors. ComputeCBufferReflection validates the member's (array-unwrapped) base type and throws if it is not a 3- or 4-component float vector.

Solutions

  1. Change the member type to float3 or float4
  2. Remove the [Color] attribute if the type must stay non-vector
  3. If a 2-component color is needed, pad to float4 and remove the attribute or use a float4

Example fix

// before
[Color] float2 Tint;
// after
[Color] float4 Tint;
Defensive patterns

Strategy: validation

Validate before calling

// SDSL source check before compiling
if (attrName == "Color" && !(type is "float3" or "float4"))
    throw new ArgumentException("[Color] requires float3/float4");

Type guard

bool IsValidColorAttr(TypeDescription t) =>
    t is VectorType { BaseType: { Type: Scalar.Float }, Size: 3 or 4 };

Try / catch

try { result = mixer.MergeSDSL(tree); }
catch (InvalidOperationException ex) when (ex.Message.Contains("[Color] attribute")) {
    reportShaderSourceError(shaderUrl, ex.Message);
}

Prevention

When it happens

Trigger: Annotating a cbuffer member with [Color] when its type is float2, float, int3, or any non-vector type; [Color] on an array whose element type is not float3/float4; a renamed/aliased type resolving to Scalar other than Float.

Common situations: Copying [Color] onto an int-based HDR or hex-color uniform; typos like `float2 Color [Color]`; shaders ported from engines where [Color] accepts other widths.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/0023f0477ba8a5c0. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Compilers/SDSL/ShaderMixer.CBuffers.cs:474

                    var metadata = cbufferMetadata[index];

                    memberInfos[index] = new EffectValueDescription
                    {
                        Type = ConvertType(context, member.Type, member.TypeModifier, SpirvBuilder.AlignmentRules.CBuffer),
                        RawName = member.Name,
                        KeyInfo = new EffectParameterKeyInfo { KeyName = metadata.Link },
                        Offset = constantBufferOffset,
                        Size = memberSize,
                        LogicalGroup = metadata.LogicalGroup,
                        DefaultValue = ConvertDefaultValue(metadata.DefaultValue),
                    };
                    if (metadata.Color)
                    {
                        var baseType = member.Type;
                        while (baseType is ArrayType arrayType)
                            baseType = arrayType.BaseType;
                        if (baseType is not VectorType { BaseType: { Type: Scalar.Float }, Size: 3 or 4 })
                            throw new InvalidOperationException("[Color] attribute can only be applied on float3/float4 vector types");
                        memberInfos[index].Type.Class = EffectParameterClass.Color;
                    }

                    // Adjust offset for next item
                    constantBufferOffset += memberSize;
                    SpirvBuilder.PadOffsetAfterArray(member.Type, member.TypeModifier, memberInfos[index].Offset, ref constantBufferOffset, SpirvBuilder.AlignmentRules.CBuffer);
                }

                var cbufferDesc = new EffectConstantBufferDescription
                {
                    Name = context.Names[cbuffer.VariableId],
                    // Round buffer size to next multiple of 16 bytes
                    Size = (constantBufferOffset + 15) / 16 * 16,

                    Type = ConstantBufferType.ConstantBuffer,
                    Members = memberInfos,
                };
                globalContext.Reflection.ConstantBuffers.Add(cbufferDesc);

View on GitHub (pinned to 96fad776d2)