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
- Use a complete terminal description: `export TERM=xterm-256color`
- Install/repair the terminfo database (ncurses-term / ncurses-terminfo) and confirm with `infocmp $TERM | grep -E 'cub|cuf'`
- If you maintain the terminfo entry, add cub1/cuf1 (or cub/cuf) capabilities to it
- 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
- Never point TERM at dumb/unknown entries when an interactive REPL will run
- Smoke-test movement capabilities during environment setup
- Keep full ncurses terminfo packages installed on dev and CI images
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
- insufficient terminal (vertical)
- terminal doesn't have the required {cap} capability
- `terminal_name` must be a string
- `terminal_name` cannot be empty
- NUL character found in `terminal_name`
AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17).
Data as JSON: /api/errors/c977b4150e83de0e.
Report an issue: GitHub.