{"record":{"id":"9854188352bd6955","repo":"AvaloniaUI/Avalonia","slug":"transform-container-stack-is-non-empty","errorCode":null,"errorMessage":"Transform container stack is non-empty","messagePattern":"Transform container stack is non-empty","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Avalonia.Base/Media/ImmediateDrawingContext.cs","lineNumber":362,"sourceCode":"                throw new ObjectDisposedException(nameof(DrawingContext));\n            _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));\n            _currentContainerTransform = CurrentTransform * _currentContainerTransform;\n            _currentTransform = Matrix.Identity;\n            return new PushedState(this, PushedState.PushedStateType.MatrixContainer);\n        }\n\n        /// <summary>\n        /// Disposes of any resources held by the <see cref=\"DrawingContext\"/>.\n        /// </summary>\n        public void Dispose()\n        {\n            if (_states is null || _transformContainers is null)\n                throw new ObjectDisposedException(nameof(DrawingContext));\n            while (_states.Count != 0)\n                _states.Peek().Dispose();\n            StateStackPool.ReturnAndSetNull(ref _states);\n            if (_transformContainers.Count != 0)\n                throw new InvalidOperationException(\"Transform container stack is non-empty\");\n            TransformStackPool.ReturnAndSetNull(ref _transformContainers);\n            if (_ownsImpl)\n                PlatformImpl.Dispose();\n        }\n\n        private static bool PenIsVisible(IPen? pen)\n        {\n            return pen?.Brush != null && pen.Thickness > 0;\n        }\n\n        public object? TryGetFeature(Type type) => PlatformImpl.GetFeature(type);\n    }\n}\n","sourceCodeStart":344,"sourceCodeEnd":376,"githubUrl":"https://github.com/AvaloniaUI/Avalonia/blob/11c542726898ae954a1ef668c65ec79ec92ab17d/src/Avalonia.Base/Media/ImmediateDrawingContext.cs#L344-L376","documentation":"ImmediateDrawingContext.Dispose throws InvalidOperationException(\"Transform container stack is non-empty\") when _transformContainers still has entries after all states are popped. Every PushTransformContainer must be matched by disposing its PushedState before the context is disposed.","triggerScenarios":"Calling PushTransformContainer and disposing the context without disposing the returned PushedState — a leaked transform-container push leaves an entry on the stack.","commonSituations":"Forgetting to using/dispose a PushTransformContainer result; an exception skipping the dispose; conditional branches that push but do not pop.","solutions":["Always wrap PushTransformContainer in a using/using-declaration.","Ensure every push has a matching pop on all code paths including exceptions.","Keep transform-container pushes lexically scoped."],"exampleFix":"// before\nctx.PushTransformContainer(); // result discarded\nctx.Dispose(); // throws: stack non-empty\n\n// after\nusing (ctx.PushTransformContainer()) { /* draw */ }\nctx.Dispose();","handlingStrategy":"validation","validationCode":"// Always pair PushTransformContainer with a using.\nusing (ctx.PushTransformContainer()) { /* draw */ }\n// Now safe to dispose the context.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wrap every PushTransformContainer in using.","Ensure matching pop on all code paths including exceptions.","Keep transform-container pushes lexically scoped."],"tags":["rendering","drawingcontext","lifecycle","invalidoperation"],"backgroundTag":null,"analyzedSha":"11c542726898ae954a1ef668c65ec79ec92ab17d","analyzedAt":"2026-08-13T11:57:40.261Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}