kovidgoyal/kitty · warning

Custom draw tab function failed with error: {e}

Error message

Custom draw tab function failed with error: {e}

What it means

A user-supplied draw_tab function (from tab_bar.py) raised at render time; kitty logs the error and falls back to the built-in draw_tab_with_fade so the tab bar still renders.

Source

Thrown at kitty/tab_bar.py:679

        log_error(f'Failed to load custom tab_bar.py module with error: {e}')
        return {}


@run_once
def load_custom_draw_tab() -> DrawTabFunc:
    m = load_custom_draw_tab_module()
    func: DrawTabFunc | None = m.get('draw_tab')
    if func is None:
        return draw_tab_with_fade

    @wraps(func)
    def draw_tab(
        draw_data: DrawData, screen: Screen, tab: TabBarData, before: int, max_tab_length: int, index: int, is_last: bool, extra_data: ExtraData
    ) -> int:
        try:
            return func(draw_data, screen, tab, before, max_tab_length, index, is_last, extra_data)
        except Exception as e:
            log_error(f'Custom draw tab function failed with error: {e}')
            return draw_tab_with_fade(draw_data, screen, tab, before, max_tab_length, index, is_last, extra_data)

    return draw_tab


def clear_caches() -> None:
    load_custom_draw_tab.clear_cached()
    load_custom_draw_tab_module.clear_cached()


class CustomDrawTitleFunc:
    def __init__(self, data: dict[str, Any], implementation: Callable[[dict[str, Any]], str] | None = None):
        self._implementation = implementation
        self._data = {} if implementation is None else data.copy()

    def __str__(self) -> str:
        if self._implementation is None:
            return ''

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Check kitty's log for the exact exception from your draw_tab
  2. Guard against edge values: max_tab_length <= 0, missing tab.title, empty extra_data.tabs
  3. Update tab_bar.py to the current kitty tab bar API and test with 0, 1, and many tabs

Example fix

# before
def draw_tab(dd, screen, tab, before, max_tab_length, index, is_last, extra_data):
    return dd.ascent + len(tab.title) // max_tab_length

# after
def draw_tab(dd, screen, tab, before, max_tab_length, index, is_last, extra_data):
    if max_tab_length < 1:
        return dd.ascent
    return dd.ascent + len(tab.title or '') // max_tab_length
Defensive patterns

Strategy: try-catch

Try / catch

def draw_tab(dd, screen, tab, before, max_tab_length, index, is_last, extra_data):
    try:
        return my_draw(dd, screen, tab, before, max_tab_length, index, is_last, extra_data)
    except Exception:
        return draw_tab_with_fade(dd, screen, tab, before, max_tab_length, index, is_last, extra_data)

Prevention

When it happens

Trigger: The wrapped custom draw_tab in tab_bar.py raises any Exception during tab bar update.

Common situations: Runtime bugs in custom tab bar code (division by zero on max_tab_length, None title, API changes to TabBarData fields) that only manifest with real tabs.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/256e16804a6eaa8f. Report an issue: GitHub.