{"record":{"id":"4e31f181d34ae22b","repo":"dotnet/wpf","slug":"specified-visual-cannot-be-detached","errorCode":null,"errorMessage":"Specified Visual cannot be detached.","messagePattern":"Specified Visual cannot be detached\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/Renderer.cs","lineNumber":325,"sourceCode":"                parent.Children.Add(visual);\n\n                // Put the visual into the list of visuals attached via this method\n                _attachedVisuals.Add(visual);\n            }\n        }\n\n        /// <summary>\n        /// Detaches a visual previously attached via AttachIncrementalRendering\n        /// </summary>\n        /// <param name=\"visual\">the visual to detach</param>\n        internal void DetachIncrementalRendering(Visual visual)\n        {\n            ArgumentNullException.ThrowIfNull(visual);\n\n            // Remove the visual in the list of attached via AttachIncrementalRendering\n            if ((_attachedVisuals == null) || (!_attachedVisuals.Remove(visual)))\n            {\n                throw new System.InvalidOperationException(SR.VisualCannotBeDetached);\n            }\n\n            // Detach it from the tree\n            DetachVisual(visual);\n        }\n\n        /// <summary>\n        /// Internal helper used to indicate if a visual was previously attached \n        /// via a call to AttachIncrementalRendering\n        /// </summary>\n        internal bool ContainsAttachedIncrementalRenderingVisual(Visual visual)\n        {\n            if (visual == null || _attachedVisuals == null)\n            {\n                return false;\n            }\n\n            return _attachedVisuals.Contains(visual);","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/Renderer.cs#L307-L343","documentation":"Renderer.DetachIncrementalRendering throws InvalidOperationException (SR.VisualCannotBeDetached) when the given Visual was never attached via AttachIncrementalRendering (or the attach list is null). Detaching an unattached visual is treated as a programming error.","triggerScenarios":"Calling DetachIncrementalRendering with a visual not in the Renderer's _attachedVisuals list — e.g. detach called twice, or a visual attached to a different Renderer instance.","commonSituations":"Double-detach in cleanup code, swapping renderers without re-attaching visuals, teardown ordering where attach never ran but detach did.","solutions":["Only call DetachIncrementalRendering for visuals previously attached to the same Renderer instance","Track attached visuals yourself and skip detach when not present","Ensure attach/detach calls are paired and ordered correctly in lifecycle code","Catch InvalidOperationException during cleanup teardown to make it idempotent"],"exampleFix":"// before\nrenderer.DetachIncrementalRendering(visual); // throws if never attached\n// after\nif (attached.Remove(visual))\n{\n    renderer.DetachIncrementalRendering(visual);\n}","handlingStrategy":"validation","validationCode":"// Only detach visuals you attached\nif (attachedVisuals.Contains(visual)) renderer.DetachIncrementalRendering(visual);","typeGuard":"bool CanDetach(Visual v) => v != null && attachedVisuals.Contains(v);","tryCatchPattern":"try { renderer.DetachIncrementalRendering(visual); }\ncatch (InvalidOperationException) { attachedVisuals.Remove(visual); /* idempotent cleanup */ }","preventionTips":["Make teardown idempotent by tracking attach state","Never call detach without a prior successful attach on the same renderer","Keep attach/detach in symmetric lifecycle methods","Log skipped detaches during cleanup instead of throwing"],"tags":["ink","renderer","visual","cleanup"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}