{"record":{"id":"9ffa9720324114e8","repo":"Textualize/rich","slug":"unable-to-pop-base-theme","errorCode":null,"errorMessage":"Unable to pop base theme","messagePattern":"Unable to pop base theme","errorType":"exception","errorClass":"ThemeStackError","httpStatus":null,"severity":"error","filePath":"rich/theme.py","lineNumber":109,"sourceCode":"\n    def push_theme(self, theme: Theme, inherit: bool = True) -> None:\n        \"\"\"Push a theme on the top of the stack.\n\n        Args:\n            theme (Theme): A Theme instance.\n            inherit (boolean, optional): Inherit styles from current top of stack.\n        \"\"\"\n        styles: Dict[str, Style]\n        styles = (\n            {**self._entries[-1], **theme.styles} if inherit else theme.styles.copy()\n        )\n        self._entries.append(styles)\n        self.get = self._entries[-1].get\n\n    def pop_theme(self) -> None:\n        \"\"\"Pop (and discard) the top-most theme.\"\"\"\n        if len(self._entries) == 1:\n            raise ThemeStackError(\"Unable to pop base theme\")\n        self._entries.pop()\n        self.get = self._entries[-1].get\n\n\nif __name__ == \"__main__\":  # pragma: no cover\n    theme = Theme()\n    print(theme.config)\n","sourceCodeStart":91,"sourceCodeEnd":117,"githubUrl":"https://github.com/Textualize/rich/blob/9d8f9a372cc5916fd4781fec207ced7ddac2f08f/rich/theme.py#L91-L117","documentation":"ThemeStack keeps a list of pushed theme dicts, always retaining the single base theme as the bottom entry. pop_theme() refuses to pop when len(_entries) == 1 — removing the base would leave the stack with no styles at all — raising ThemeStackError('Unable to pop base theme').","triggerScenarios":"Calling console.pop_theme() more times than push_theme() was called (unbalanced push/pop); popping on a fresh stack that never pushed; an exception path between push_theme and pop_theme that double-pops (e.g. finally-block pop after an early pop).","commonSituations":"Temporarily applying a theme around a code block (push/pop in try/finally) where a nested routine also pops; context-manager helper that pops in __exit__ being used without a matching enter; copy-paste of the push/pop snippet without the push line.","solutions":["Match every pop_theme() with exactly one push_theme(); prefer a try/finally around the themed block.","Track nesting depth yourself, or guard: pop only if you know you pushed (pass/return a token).","If you need the base theme back without popping, just push the default theme instead of popping past the base."],"exampleFix":"# before\nconsole.pop_theme()  # extra pop -> ThemeStackError\n\n# after\nconsole.push_theme(my_theme)\ntry:\n    ...\nfinally:\n    console.pop_theme()  # exactly one pop per push","handlingStrategy":"try-catch","validationCode":"from rich.theme import ThemeStackError\n# count pushes if you must pop conditionally\npushed = getattr(console, '_thread_pushes', 0)  # not exposed; track yourself:","typeGuard":null,"tryCatchPattern":"from rich.theme import ThemeStackError\ntry:\n    console.pop_theme()\nexcept ThemeStackError:\n    pass  # already at base theme","preventionTips":["Wrap themed sections in try/finally with exactly one push_theme/pop_theme pair.","Catch ThemeStackError defensively in cleanup code that might run twice.","Use a contextmanager helper to guarantee push/pop balance."],"tags":["rich","theme","stack-underflow","push-pop-balance","themestackerror"],"backgroundTag":null,"analyzedSha":"9d8f9a372cc5916fd4781fec207ced7ddac2f08f","analyzedAt":"2026-08-15T03:30:11.781Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}