dotnet/wpf · error · ArgumentException

Unrecognized Stroke in…

Error message

Unrecognized Stroke in StrokeCollectionChangedEventArgs.Removed.

What it means

Renderer.OnStrokesChanged throws ArgumentException (SR.UnknownStroke3) when a Removed notification references a stroke that the Renderer has no visual for. The renderer can only detach visuals for strokes it previously tracked via Added events.

Solutions

  1. Keep one StrokeCollection per Renderer and don't mutate it from outside its owner
  2. Ensure the stroke was Added (and the add event processed) before Removing
  3. Avoid synthetic StrokeCollectionChangedEventArgs with strokes the renderer never saw
  4. Re-sync by rebuilding the StrokeCollection from strokes known to the renderer

Example fix

// before
args = new StrokeCollectionChangedEventArgs(new[] { orphanStroke }, new StrokeCollection());
// after
if (tracked.Contains(orphanStroke)) { tracked.Remove(orphanStroke); }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the stroke belongs to the collection before removing
if (collection.Contains(stroke)) collection.Remove(stroke);

Type guard

bool CanRemoveSafely(StrokeCollection c, Stroke s) => c != null && c.Contains(s);

Try / catch

try { collection.Remove(stroke); }
catch (ArgumentException ex) { logger.LogWarning(ex, "Stroke not tracked by renderer"); }

Prevention

When it happens

Trigger: A Stroke in StrokeCollectionChangedEventArgs.Removed was never part of the Renderer's _visuals dictionary — e.g. the stroke was removed from a collection it was never added to (on that renderer), or state was desynchronized.

Common situations: Sharing a StrokeCollection across renderers/threads where events were missed, manually rebuilding collections and firing synthetic change args, suspending/resuming event listening causing missed Adds.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/Renderer.cs:464

                // Attach it to the visual tree
                AttachVisual(visual, buildingStrokeCollection: false);
            }

            // Deal with removed strokes first
            foreach (Stroke stroke in removed)
            {
                // Verify that the event is in sync with the view
                StrokeVisual visual = null;
                if (_visuals.TryGetValue(stroke, out visual))
                {
                    // get rid of both the visual and the stroke
                    DetachVisual(visual);
                    StopListeningOnStrokeEvents(visual.Stroke);
                    _visuals.Remove(stroke);
                }
                else
                {
                    throw new System.ArgumentException(SR.UnknownStroke3);
                }
            }
        }

        /// <summary>
        /// Stroke Invalidated event handler
        /// </summary>
        private void OnStrokeInvalidated(object sender, EventArgs eventArgs)
        {
            System.Diagnostics.Debug.Assert(_strokes.IndexOf(sender as Stroke) != -1);

            // Find the visual associated with the changed stroke.
            StrokeVisual visual;
            Stroke stroke = (Stroke)sender;
            if (!_visuals.TryGetValue(stroke, out visual))
            {
                throw new System.ArgumentException(SR.UnknownStroke1);
            }

View on GitHub (pinned to 81131a70a4)