dotnet/wpf · error · ArgumentException

SR.MatrixNotInvertible

Error message

SR.MatrixNotInvertible

What it means

Stroke.Transform(Matrix) requires an invertible matrix so it can compute the inverse transform (needed to adjust things like stroke thickness when only stroke geometry is transformed). If matrix.HasInverse is false — i.e. the matrix is singular (zero determinant) — an ArgumentException naming transformMatrix is thrown and the stroke is left unchanged.

Solutions

  1. Check transformMatrix.HasInverse before calling Transform and pick a fallback matrix otherwise
  2. Ensure scale factors are non-zero; fix the code that computed the matrix so determinant != 0
  3. Call Matrix.Invert in a try/catch upstream to detect singular matrices early

Example fix

// before
stroke.Transform(matrix); // throws when singular
// after
if (!matrix.HasInverse) { matrix = Matrix.Identity; }
stroke.Transform(matrix);
Defensive patterns

Strategy: validation

Validate before calling

if (!matrix.HasInverse) { matrix = Matrix.Identity; } stroke.Transform(matrix);

Type guard

bool IsUsableTransform(Matrix m) => m.HasInverse && !MatrixHelper.ContainsNaN(m) && !MatrixHelper.ContainsInfinity(m);

Try / catch

try { stroke.Transform(m); } catch (ArgumentException ex) { log(ex); m = Matrix.Identity; stroke.Transform(m); }

Prevention

When it happens

Trigger: Calling stroke.Transform(m) or InkCanvas/Strokes transforms with a singular Matrix, most typically new Matrix() (identity fields zeroed via SetIdentity misuse or default struct) or a scale/combined matrix with a zero scale factor (e.g. scaleX=0).

Common situations: Computing a transform from UI measurements where a dimension is 0 (width 0 element driving a scale); accumulating matrices leading to determinant 0; unit tests constructing Matrix via new Matrix() without SetIdentity.

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/bc6cb23faac7907c. Report an issue: GitHub.

Appendix: source

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

            clone._cloneStylusPoints = true;

            return clone;
        }


        /// <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)

View on GitHub (pinned to 81131a70a4)