Textualize/textual · error · ValueError

'pane_id' must be a non-empty string or a ContentTab with an

Error message

'pane_id' must be a non-empty string or a ContentTab with an id.

What it means

Raised by TabbedContent.get_pane when pane_id is neither a non-empty string nor a ContentTab with a truthy id. get_pane resolves the id against the internal ContentSwitcher, so it cannot proceed without an id.

Source

Thrown at src/textual/widgets/_tabbed_content.py:620

        Returns:
            The `TabPane` associated with the ID or the given tab.

        Raises:
            ValueError: Raised if no ID was available.
        """
        target_id: str | None = None
        if isinstance(pane_id, ContentTab):
            target_id = (
                pane_id.id if pane_id.id is None else ContentTab.sans_prefix(pane_id.id)
            )
        else:
            target_id = pane_id
        if target_id:
            pane = self.get_child_by_type(ContentSwitcher).get_child_by_id(target_id)
            assert isinstance(pane, TabPane)
            return pane
        raise ValueError(
            "'pane_id' must be a non-empty string or a ContentTab with an id."
        )

    def _on_tabs_tab_disabled(self, event: Tabs.TabDisabled) -> None:
        """Disable the corresponding tab pane."""
        if event.tabs.parent is not self:
            return
        event.stop()
        tab_id = event.tab.id or ""
        try:
            with self.prevent(TabPane.Disabled):
                self.get_child_by_type(ContentSwitcher).get_child_by_id(
                    ContentTab.sans_prefix(tab_id), expect_type=TabPane
                ).disabled = True
        except NoMatches:
            return

    def _on_tab_pane_disabled(self, event: TabPane.Disabled) -> None:

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Always set ids on Tabs/TabPanes so the whole TabbedContent machinery works.
  2. Check `tab.id` before calling get_pane, or pass the pane's string id directly.
  3. Validate user-provided ids are non-empty.

Example fix

# before
tabbed_content.get_pane(some_tab)  # some_tab.id is None
# after
if some_tab.id:
    pane = tabbed_content.get_pane(some_tab)
Defensive patterns

Strategy: validation

Validate before calling

pid = pane_id.id if isinstance(pane_id, ContentTab) else pane_id
if pid:
    pane = tabbed_content.get_pane(pid)

Try / catch

try:
    pane = tabbed_content.get_pane(pane_id)
except ValueError:
    pane = None

Prevention

When it happens

Trigger: get_pane('') or get_pane(tab) where tab.id is None; also called internally via active_pane when the active tab has no id.

Common situations: Looking up panes by ContentTab objects whose id was never set, or generating ids from user input that can be empty.

Related errors


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