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
- Normalize before assigning: quat = SQuaternion.Normalize(quat);
- If constructing from axis/angle or basis vectors, use the proper constructor instead of raw components.
- 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
- Always Normalize after lerping/slerping or accumulating rotations
- Build quaternions from axis-angle constructors rather than raw components
- Normalize deserialized rotation data on load
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
- Url must be valid, assign null instead
- 's must be finite
- 's must be finite and greater than zero
- The provided path is not a valid path name.
- Build manifest [ ] doesn't exist
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)