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
- Keep one StrokeCollection per Renderer and don't mutate it from outside its owner
- Ensure the stroke was Added (and the add event processed) before Removing
- Avoid synthetic StrokeCollectionChangedEventArgs with strokes the renderer never saw
- 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
- Keep one StrokeCollection per Renderer
- Don't raise synthetic change events with untracked strokes
- Process add events before allowing removes
- Re-sync renderer state after bulk collection rebuilds
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
- Duplicate Stroke in StrokeCollectionChangedEventArgs.Added.
- Unrecognized Stroke in Stroke.Invalidated event arguments.
- Cannot attach a Visual that is already attached.
- SR.EventArgIsNull
- SR.EventArgIsNull
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)