dotnet/wpf · error · ArgumentException

SR.InvalidMatrixContainsNaN

Error message

SR.InvalidMatrixContainsNaN

What it means

Stroke.Transform(Matrix) rejects matrices containing NaN values via MatrixHelper.ContainsNaN. A NaN matrix has no meaningful geometric interpretation and would corrupt cached geometry and bounds, so an ArgumentException naming transformMatrix is thrown before any state is modified.

Solutions

  1. Validate with MatrixHelper.ContainsNaN (or double.IsNaN on M11..OffsetY) before calling Transform and reject/repair the source values
  2. Trace upstream arithmetic (divisions, layout values) that produce NaN and fix at the source
  3. Replace the NaN matrix with a valid default (e.g. Matrix.Identity) when NaN indicates missing data

Example fix

// before
stroke.Transform(computedMatrix); // may contain NaN
// after
if (!MatrixHelper.ContainsNaN(computedMatrix) && !MatrixHelper.ContainsInfinity(computedMatrix))
    stroke.Transform(computedMatrix);
Defensive patterns

Strategy: validation

Validate before calling

bool ok = !MatrixHelper.ContainsNaN(matrix) && !MatrixHelper.ContainsInfinity(matrix) && matrix.HasInverse;

Type guard

bool IsFiniteMatrix(Matrix m) => double.IsFinite(m.M11) && double.IsFinite(m.M12) && double.IsFinite(m.M21) && double.IsFinite(m.M22) && double.IsFinite(m.OffsetX) && double.IsFinite(m.OffsetY);

Prevention

When it happens

Trigger: Calling stroke.Transform(m) where any of m's six doubles is NaN — typically the result of 0/0, Math.Atan of invalid input, or a transform derived from a divide-by-zero scale computation.

Common situations: Data-binding or measurement code producing NaN widths/heights (e.g. NaN from ActualWidth before layout) that flow into matrix construction; deserializing matrices from corrupted data.

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 dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/83065030155eb5ba. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Ink/Stroke.cs:148


        /// <summary>Transforms the ink and also changes the StylusTip</summary>
        /// <param name="transformMatrix">Matrix to transform the stroke by</param>
        /// <param name="applyToStylusTip">Boolean if true the transform matrix will be applied to StylusTip</param>
        public virtual void Transform(Matrix transformMatrix, bool applyToStylusTip)
        {
            if (transformMatrix.IsIdentity)
            {
                return;
            }

            if (!transformMatrix.HasInverse)
            {
                throw new ArgumentException(SR.MatrixNotInvertible, nameof(transformMatrix));
            }
            else if ( MatrixHelper.ContainsNaN(transformMatrix))
            {
                throw new ArgumentException(SR.InvalidMatrixContainsNaN, nameof(transformMatrix));
            }
            else if ( MatrixHelper.ContainsInfinity(transformMatrix))
            {
                throw new ArgumentException(SR.InvalidMatrixContainsInfinity, nameof(transformMatrix));
            }
            else
            {
                // we need to force a recaculation of the cached path geometry right after the
                // DrawingAttributes changed, beforet the events are raised.
                _cachedGeometry = null;
                // Set the cached bounds to empty, which will force a re-calculation of the _cachedBounds upon next GetBounds call.
                _cachedBounds = Rect.Empty;

                if (applyToStylusTip)
                {
                    //we use this flag to prevent this method from causing two
                    //invalidates, which causes a good deal of memory thrash when
                    //the strokes are being rendered

View on GitHub (pinned to 81131a70a4)