dotnet/wpf · error · InvalidOperationException

SR.EndHitTestingCalled

Error message

SR.EndHitTestingCalled

What it means

After EndHitTesting() is called the IncrementalHitTester is invalid; further AddPoints calls throw InvalidOperationException (SR.EndHitTestingCalled). This enforces the hit-tester's lifecycle: create → test → EndHitTesting.

Solutions

  1. Create a new IncrementalHitTester (via stroke.GetIncrementalHitTester/GetIncrementalStrokeHitTester) after EndHitTesting instead of reusing the old one
  2. Track tester validity with a flag or check and skip AddPoints once invalidated
  3. Wrap in try-catch InvalidOperationException to tolerate post-dispose events

Example fix

// before
if (_hitTester != null) _hitTester.AddPoints(pts);
// after
if (_hitTester != null && _hitTesterValid) _hitTester.AddPoints(pts);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!_hitTesterValid) return;

Type guard

bool CanTest(IncrementalHitTester t, bool valid) => t != null && valid;

Try / catch

try { tester.AddPoints(pts); } catch (InvalidOperationException) { _hitTesterValid = false; }

Prevention

When it happens

Trigger: Calling AddPoints/AddPoint after EndHitTesting() on the same tester — e.g. reusing a tester stored in a field across stroke re-render or after cancellation.

Common situations: Long-lived hit-tester fields not reset when a new stroke erase begins; event handlers firing after hit-testing was concluded; async continuations running after EndHitTesting.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/3740b41c75f70d99. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Ink/IncrementalHitTester.cs:46

            AddPoints(new Point[1] { point });
        }

        /// <summary>
        /// Adds an array of points representing an incremental move of the hit-testing tool
        /// </summary>
        /// <param name="points">points representing an incremental move of the hitting tool</param>
        public void AddPoints(IEnumerable<Point> points)
        {
            ArgumentNullException.ThrowIfNull(points);

            if (IEnumerablePointHelper.GetCount(points) == 0)
            {
                throw new System.ArgumentException(SR.EmptyArrayNotAllowedAsArgument, nameof(points));
            }

            if (!_fValid)
            {
                throw new System.InvalidOperationException(SR.EndHitTestingCalled);
            }

            System.Diagnostics.Debug.Assert(_strokes != null);

            AddPointsCore(points);
        }

        /// <summary>
        /// Adds a StylusPacket representing an incremental move of the hit-testing tool
        /// </summary>
        /// <param name="stylusPoints">stylusPoints</param>
        public void AddPoints(StylusPointCollection stylusPoints)
        {
            ArgumentNullException.ThrowIfNull(stylusPoints);

            if (stylusPoints.Count == 0)
            {
                throw new System.ArgumentException(SR.EmptyArrayNotAllowedAsArgument, nameof(stylusPoints));

View on GitHub (pinned to 81131a70a4)