stride3d/stride · error · ArgumentOutOfRangeException

's must be normalized

Error message

{owner}'s {fieldName} must be normalized

What it means

ValidateRange(this SQuaternion, ...) verifies the quaternion is normalized (unit length). The physics engine expects rotation quaternions to be unit quaternions; an unnormalized one skews rotations and solver math, so it throws ArgumentOutOfRangeException naming the owner and field.

Solutions

  1. Normalize before assigning: quat = SQuaternion.Normalize(quat);
  2. If constructing from axis/angle or basis vectors, use the proper constructor instead of raw components.
  3. Validate/normalize quaternion data at load time for deserialized assets.

Example fix

// before
body.Orientation = lerpedQuat; // length != 1
// after
body.Orientation = SQuaternion.Normalize(lerpedQuat);
Defensive patterns

Strategy: validation

Validate before calling

if (Math.Abs(quat.Length() - 1f) > 1e-4f) {
    quat = SQuaternion.Normalize(quat);
}

Try / catch

try { quat.ValidateRange(this); body.Orientation = quat; } catch (ArgumentOutOfRangeException) { body.Orientation = SQuaternion.Normalize(quat); }

Prevention

When it happens

Trigger: Assigning a quaternion built from raw/unnormalized data (lerp without normalize, deserialized rotation, manual component math) to a physics-facing property guarded by this extension.

Common situations: Interpolating rotations without calling Normalize, loading quaternions from files/multiplied accumulations that drift from unit length, or constructing from non-orthonormal basis vectors.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/Definitions/BepuAndStrideExtensions.cs:49

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SVector3 ToStride(this NVector3 vec) => Unsafe.As<NVector3, SVector3>(ref vec);
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static NQuaternion ToNumeric(this SQuaternion qua) => Unsafe.As<SQuaternion, NQuaternion>(ref qua);
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static SQuaternion ToStride(this NQuaternion qua) => Unsafe.As<NQuaternion, SQuaternion>(ref qua);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void ValidateRange<T>(this SVector3 vec, T owner, [CallerMemberName] string? fieldName = null)
    {
        if (NVector3.AllWhereAllBitsSet(NVector3.IsFinite(vec)) == false)
            throw new ArgumentOutOfRangeException($"{owner}'s {fieldName} must be finite");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void ValidateRange<T>(this SQuaternion quat, T owner, [CallerMemberName] string? fieldName = null)
    {
        if (quat.IsNormalized == false)
            throw new ArgumentOutOfRangeException($"{owner}'s {fieldName} must be normalized");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void ValidateGreaterThanZeroFinite<T>(this float fp, T owner, [CallerMemberName] string? fieldName = null)
    {
        if (fp <= 0 || float.IsFinite(fp) == false)
            throw new ArgumentOutOfRangeException($"{owner}'s {fieldName} must be finite and greater than zero");
    }
}

View on GitHub (pinned to 96fad776d2)