{"record":{"id":"2cae644fd733ddb4","repo":"Textualize/textual","slug":"widget-children-is-read-only-use-widget-mount","errorCode":null,"errorMessage":"Widget.children is read-only: use Widget.mount(...) or Widget.remove(...) to add or remove widgets","messagePattern":"Widget\\.children is read-only: use Widget\\.mount\\(\\.\\.\\.\\) or Widget\\.remove\\(\\.\\.\\.\\) to add or remove widgets","errorType":"exception","errorClass":"ReadOnlyError","httpStatus":null,"severity":"error","filePath":"src/textual/_node_list.py","lineNumber":233,"sourceCode":"        \"\"\"Just the nodes where `display==True`, in reverse order.\"\"\"\n        return filter(_display_getter, reversed(self._nodes))\n\n    if TYPE_CHECKING:\n\n        @overload\n        def __getitem__(self, index: int) -> Widget: ...\n\n        @overload\n        def __getitem__(self, index: slice) -> list[Widget]: ...\n\n    def __getitem__(self, index: int | slice) -> Widget | list[Widget]:\n        return self._nodes[index]\n\n    if not TYPE_CHECKING:\n        # This confused the type checker for some reason\n        def __getattr__(self, key: str) -> object:\n            if key in {\"clear\", \"append\", \"pop\", \"insert\", \"remove\", \"extend\"}:\n                raise ReadOnlyError(\n                    \"Widget.children is read-only: use Widget.mount(...) or Widget.remove(...) to add or remove widgets\"\n                )\n            raise AttributeError(key)\n","sourceCodeStart":215,"sourceCodeEnd":237,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/_node_list.py#L215-L237","documentation":"Widget.children returns a NodeList that intentionally blocks the standard list mutation methods (clear, append, pop, insert, remove, extend) via __getattr__, raising ReadOnlyError. Textual requires the widget tree to be mutated through its lifecycle APIs so that mounting, unmounting, refreshing, and layout are handled correctly. Any attempt to treat children like a plain Python list triggers this error immediately.","triggerScenarios":"Calling widget.children.append(child), widget.children.remove(child), widget.children.pop(), widget.children.clear(), or widget.children.insert(...) anywhere in app code; passing widget.children to helper code that mutates lists in place.","commonSituations":"Developers coming from other UI frameworks where child lists are mutable; trying to reorder children by sorting the list in place; clean-up code that clears children instead of calling remove(); copy-pasting list-manipulation helpers onto NodeList.","solutions":["Use await widget.mount(...) to add children","Use await widget.remove_children(...) / await child.remove() to remove them","Use await widget.remove_children(...) followed by mount(...) (or the replace_children convenience) to reset or reorder children","For reordering, remove and re-mount the affected widgets rather than mutating the list"],"exampleFix":"# before\nself.children.append(Static(\"hi\"))\nself.children.remove(old)\n\n# after\nawait self.mount(Static(\"hi\"))\nawait old.remove()\n# or: await self.remove_children() then await self.mount(...)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"from textual._node_list import NodeList\n\ndef is_read_only_node_list(obj) -> TypeGuard[NodeList]:\n    return isinstance(obj, NodeList)  # treat as read-only; never mutate","tryCatchPattern":"from textual._node_list import ReadOnlyError\ntry:\n    widget.children.append(child)\nexcept ReadOnlyError:\n    await widget.mount(child)","preventionTips":["Never call list-mutation methods on widget.children","Use mount/remove_children/replace_children for all tree edits","Type helper params as Sequence[Widget], not list[Widget], to prevent in-place mutation"],"tags":["textual","widget-tree","read-only","children"],"backgroundTag":"immutable-collection-mutation","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}