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

  1. Use await widget.mount(...) to add children
  2. Use await widget.remove_children(...) / await child.remove() to remove them
  3. Use await widget.remove_children(...) followed by mount(...) (or the replace_children convenience) to reset or reorder children
  4. 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

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


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/2cae644fd733ddb4. Report an issue: GitHub.