RustPython/RustPython · error · RuntimeError

insufficient terminal (horizontal)

Error message

insufficient terminal (horizontal)

What it means

During UnixConsole.__setup_movement, horizontal cursor movement needs one of: hpa (permanently disabled by the `0 and` guard), cub+cuf, or cub1+cuf1. If the terminfo entry provides none of these pairs, RuntimeError('insufficient terminal (horizontal)') stops console init. The terminal description is too minimal to reposition the cursor left/right, which the editor display requires.

Source

Thrown at Lib/_pyrepl/unix_console.py:612

    def __enable_bracketed_paste(self) -> None:
        os.write(self.output_fd, b"\x1b[?2004h")

    def __disable_bracketed_paste(self) -> None:
        os.write(self.output_fd, b"\x1b[?2004l")

    def __setup_movement(self):
        """
        Set up the movement functions based on the terminal capabilities.
        """
        if 0 and self._hpa:  # hpa don't work in windows telnet :-(
            self.__move_x = self.__move_x_hpa
        elif self._cub and self._cuf:
            self.__move_x = self.__move_x_cub_cuf
        elif self._cub1 and self._cuf1:
            self.__move_x = self.__move_x_cub1_cuf1
        else:
            raise RuntimeError("insufficient terminal (horizontal)")

        if self._cuu and self._cud:
            self.__move_y = self.__move_y_cuu_cud
        elif self._cuu1 and self._cud1:
            self.__move_y = self.__move_y_cuu1_cud1
        else:
            raise RuntimeError("insufficient terminal (vertical)")

        if self._dch1:
            self.dch1 = self._dch1
        elif self._dch:
            self.dch1 = terminfo.tparm(self._dch, 1)
        else:
            self.dch1 = None

        if self._ich1:
            self.ich1 = self._ich1
        elif self._ich:

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Use a complete terminal description: `export TERM=xterm-256color`
  2. Install/repair the terminfo database (ncurses-term / ncurses-terminfo) and confirm with `infocmp $TERM | grep -E 'cub|cuf'`
  3. If you maintain the terminfo entry, add cub1/cuf1 (or cub/cuf) capabilities to it
  4. Catch RuntimeError from console init and degrade to line editing without cursor control

Example fix

# before
$ TERM=dumb python -m _pyrepl.simple_interact  # RuntimeError: insufficient terminal (horizontal)
# after
$ TERM=xterm python -m _pyrepl.simple_interact
Defensive patterns

Strategy: validation

Validate before calling

import os, curses

curses.setupterm(os.environ.get('TERM') or 'xterm')
has_h = (curses.tigetstr('cub') and curses.tigetstr('cuf')) or \
         (curses.tigetstr('cub1') and curses.tigetstr('cuf1'))
if not has_h:
    raise SystemExit('terminal lacks horizontal movement caps (cub/cuf/cub1/cuf1); use TERM=xterm')

Try / catch

try:
    console = UnixConsole()
except RuntimeError as e:
    if 'insufficient terminal' in str(e):
        raise SystemExit('set TERM to a full entry, e.g. xterm-256color') from e
    raise

Prevention

When it happens

Trigger: TERM=dumb or a synthetic/minimal terminfo entry lacking cursor-movement caps; running the REPL against a stripped entry on a capture pipe or test pty; a corrupt or truncated terminfo database where movement caps are missing.

Common situations: Headless test harnesses that fabricate a tiny terminfo entry; embedded devices with hand-rolled termcap; alpine-style images that ship only dumb entries; over-customized TERM entries in restricted environments.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/c977b4150e83de0e. Report an issue: GitHub.