stride3d/stride · error · InvalidOperationException

Swizzle is out of bound for expression of type

Error message

Swizzle {Accessors[i]} is out of bound for expression {ToString(i)} of type {vectorOrScalarType}

What it means

During swizzle coalescing, each character of the first swizzle is converted to an index; if that index is >= the vector's component size, the swizzle reads components that don't exist, so an InvalidOperationException is thrown naming the accessor and expression type.

Solutions

  1. Fix the swizzle letters so all indices are within the vector size (e.g. v.xy for a float2)
  2. Verify the declared type of the variable and adjust the swizzle accordingly
  3. Add a static/validation pass to reject out-of-bounds swizzles before codegen

Example fix

// before
float2 v; float f = v.z;
// after
float2 v; float f = v.y;
Defensive patterns

Strategy: validation

Validate before calling

bool valid = swizzle.All(c => "xyzw".IndexOf(c) < vectorSize) && swizzle.All(c => c is 'x' or 'y' or 'z' or 'w');

Type guard

bool SwizzleInBounds(string swizzle, int size) => swizzle.All(c => "xyzw".IndexOf(c) < size);

Try / catch

try { Compile(expr); } catch (InvalidOperationException e) when (e.Message.Contains("is out of bound")) { ReportSwizzleError(expr, e.Message); }

Prevention

When it happens

Trigger: A shader expression like float2 v; v.xz where swizzle index (z=2) >= size (2) — i.e. any swizzle accessor beyond the vector's dimension, encountered while merging nested swizzles in Expression.cs.

Common situations: Copy-pasted GLSL code assuming vec4-sized swizzles on a vec2/vec3; dynamic shader generation producing invalid swizzle letters.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs:917

                && currentValueType is PointerType { BaseType: VectorType or ScalarType } or VectorType or ScalarType
                && Accessors[i] is Identifier { Name: var swizzle1 } id1 && id1.IsVectorSwizzle()
                && Accessors[i + 1] is Identifier { Name: var swizzle2 } id2 && id2.IsVectorSwizzle())
            {
                var vectorOrScalarType = currentValueType is PointerType p ? p.BaseType : currentValueType;

                (var size, ScalarType baseType) = vectorOrScalarType switch
                {
                    ScalarType s => (1, s),
                    VectorType v => (v.Size, v.BaseType),
                    _ => throw new NotSupportedException($"Unsupported type {vectorOrScalarType} for swizzle coalescing"),
                };

                var swizzleIndices = new int[swizzle1.Length];
                for (int j = 0; j < swizzle1.Length; ++j)
                {
                    swizzleIndices[j] = ConvertSwizzle(swizzle1[j]);
                    if (swizzleIndices[j] >= size)
                        throw new InvalidOperationException($"Swizzle {Accessors[i]} is out of bound for expression {ToString(i)} of type {vectorOrScalarType}");
                }

                // Combine swizzles with previous ones
                var newSwizzleIndices = new int[swizzle2.Length];
                for (int j = 0; j < swizzle2.Length; ++j)
                {
                    newSwizzleIndices[j] = swizzleIndices[ConvertSwizzle(swizzle2[j])];
                    if (newSwizzleIndices[j] >= size)
                        throw new InvalidOperationException($"Swizzle {Accessors[i + 1]} is out of bound for expression {ToString(i)} of type {currentValueType}");
                }

                Accessors.RemoveAt(i + 1);
                Span<char> vectorFields = ['x', 'y', 'z', 'w'];
                Span<char> newSwizzle = stackalloc char[swizzle2.Length];
                for (int j = 0; j < swizzle2.Length; ++j)
                {
                    newSwizzle[j] = vectorFields[newSwizzleIndices[j]];
                }

View on GitHub (pinned to 96fad776d2)