{"record":{"id":"07d08bc719dec41a","repo":"dotnet/wpf","slug":"sr-drawingcontext-toomanypops","errorCode":null,"errorMessage":"SR.DrawingContext_TooManyPops","messagePattern":"SR\\.DrawingContext_TooManyPops","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/DrawingDrawingContext.cs","lineNumber":1052,"sourceCode":"            _currentDrawingGroup.BitmapEffectInput = effectInput ?? new BitmapEffectInput();\n}\n\n        /// <summary>\n        /// Pop\n        /// </summary>\n        public override void Pop()\n        {\n        #if DEBUG\n            MediaTrace.DrawingContextOp.Trace(\"Pop\");\n        #endif\n\n            VerifyApiNonstructuralChange();\n\n            // Verify that Pop hasn't been called too many times\n            if ( (_previousDrawingGroupStack == null) ||\n                 (_previousDrawingGroupStack.Count == 0))\n            {\n                throw new InvalidOperationException(SR.DrawingContext_TooManyPops);\n            }\n\n            // Restore the previous value of the current drawing group\n            _currentDrawingGroup = _previousDrawingGroupStack.Pop();\n        }\n\n        /// <summary>\n        /// Draw a GlyphRun.\n        /// </summary>\n        /// <param name=\"foregroundBrush\">Foreground brush to draw GlyphRun with. </param>\n        /// <param name=\"glyphRun\"> The GlyphRun to draw. </param>\n        /// <exception cref=\"ObjectDisposedException\">\n        /// This call is illegal if this object has already been closed or disposed.\n        /// </exception>\n        public override void DrawGlyphRun(Brush foregroundBrush, GlyphRun glyphRun)\n        {\n#if DEBUG\n            MediaTrace.DrawingContextOp.Trace(\"DrawGlyphRun(constant)\");","sourceCodeStart":1034,"sourceCodeEnd":1070,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/DrawingDrawingContext.cs#L1034-L1070","documentation":"DrawingContext.Pop() was called more times than Push(). Pop restores the drawing group saved by the last Push; when _previousDrawingGroupStack is null or empty there is nothing to restore, so DrawingDrawingContext throws InvalidOperationException with SR.DrawingContext_TooManyPops. Every Push must be balanced by exactly one Pop.","triggerScenarios":"Calling ctx.Pop() when no matching ctx.Push(...) was issued, or calling Pop twice for one Push, typically inside DrawingVisual/DrawingGroup render code that builds a DrawingContext manually.","commonSituations":"Conditional Push followed by unconditional Pop (Push skipped inside an if but Pop always runs); exception thrown between Push and Pop skipping one branch; copying sample code where Push count was miscounted; DisposeCore calling Pop on behalf of an unbalanced context.","solutions":["Count Push/Pop pairs and ensure every Pop has a matching Push on the same code path","Wrap the Push/Pop pair in try/finally so Pop only runs when Push succeeded","Verify custom DisposeCore/cleanup logic does not call Pop unconditionally","Prefer Push-guid APIs (e.g. PushOpacity, PushTransform) whose Disposer pattern auto-balances the pop"],"exampleFix":"// before\nif (needClip)\n    ctx.PushClip(clipGeometry);\ncode(ctx);\nctx.Pop(); // throws when needClip == false\n\n// after\nbool pushed = false;\nif (needClip)\n{\n    ctx.PushClip(clipGeometry);\n    pushed = true;\n}\ntry { code(ctx); }\nfinally { if (pushed) ctx.Pop(); }","handlingStrategy":"validation","validationCode":"int pushDepth = 0;\nvoid SafePush(DrawingContext ctx, Action<DrawingContext> draw)\n{\n    ctx.PushTransform(Transform.Identity);\n    pushDepth++;\n    try { draw(ctx); }\n    finally { ctx.Pop(); pushDepth--; }\n}\n// assert pushDepth == 0 before calling Pop manually","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use try/finally around every Push/Pop pair","Track push depth in a counter and assert balance before manual Pop","Prefer built-in PushXxx APIs with the disposable balancer pattern","Never call Pop in DisposeCore unless your own Push succeeded"],"tags":["wpf","drawingcontext","invalid-operation","stack-imbalance"],"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-21T21:30:21.729Z"}