Textualize/textual · error · ReadOnlyError
Widget.children is read-only: use Widget.mount(...) or Widge
Error message
Widget.children is read-only: use Widget.mount(...) or Widget.remove(...) to add or remove widgets
What it means
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.
Source
Thrown at src/textual/_node_list.py:233
"""Just the nodes where `display==True`, in reverse order."""
return filter(_display_getter, reversed(self._nodes))
if TYPE_CHECKING:
@overload
def __getitem__(self, index: int) -> Widget: ...
@overload
def __getitem__(self, index: slice) -> list[Widget]: ...
def __getitem__(self, index: int | slice) -> Widget | list[Widget]:
return self._nodes[index]
if not TYPE_CHECKING:
# This confused the type checker for some reason
def __getattr__(self, key: str) -> object:
if key in {"clear", "append", "pop", "insert", "remove", "extend"}:
raise ReadOnlyError(
"Widget.children is read-only: use Widget.mount(...) or Widget.remove(...) to add or remove widgets"
)
raise AttributeError(key)
View on GitHub (pinned to 06dbeef4bb)
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
Example fix
# before
self.children.append(Static("hi"))
self.children.remove(old)
# after
await self.mount(Static("hi"))
await old.remove()
# or: await self.remove_children() then await self.mount(...) Defensive patterns
Strategy: type-guard
Type guard
from textual._node_list import NodeList
def is_read_only_node_list(obj) -> TypeGuard[NodeList]:
return isinstance(obj, NodeList) # treat as read-only; never mutate Try / catch
from textual._node_list import ReadOnlyError
try:
widget.children.append(child)
except ReadOnlyError:
await widget.mount(child) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Can't set {obj}.{self.name!r}; reactive attributes with a co
- Can't animate attribute {attribute!r} on {obj!r}; attribute
- Don't know how to animate {value!r}; Can only animate <int>,
- Can't encode {datum!r}
- must be bytes
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/2cae644fd733ddb4.
Report an issue: GitHub.