How a missing TERM entry broke my UDM-Pro shell for years
I have been on Linux for 16+ years, and yesterday I learned something about
TERM that I really should have learned a long time ago. This is the story
of how an unrelated AdGuard Home deploy on a couple of new Raspberry Pi
5’s solved a problem on my UDM-Pro that had been quietly annoying me for
years.
The AdGuard Home symptom
I was bringing up two new Pi 5’s as DNS hosts and went to check the service:
root@dns03:~# systemctl status AdGuardHome.service
WARNING: terminal is not fully functional
Press RETURN to continue
ESC[A● AdGuardHome.service - AdGuard Home: Network-level blocker
Loaded: loaded (/etc/systemd/system/AdGuardHome.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-04-25 13:32:20 MDT; 19s ago
Main PID: 1477 (AdGuardHome)
The WARNING: terminal is not fully functional line stopped me. In 16+
years on Linux I had somehow never seen it before, so I asked Claude what
it meant. The answer was the kind of answer where you immediately feel a
little dumb:
That warning isn’t from AdGuard Home, it’s from
less, the pager thatsystemctl statuspipes its output through.lessis checking yourTERMenvironment variable to decide what terminal capabilities (cursor movement, color, etc.) it can use, and it’s getting back something it considers unusable. It falls back to a degraded mode and prints that warning to let you know fancy navigation is off. The literalESC[Ayou see in the output is the same thing.lesscouldn’t translate the up-arrow key into anything useful and just printed the raw escape bytes.
I’ll be honest, I hadn’t even noticed the ESC[A in the output until
Claude pointed it out. My up-arrow had eaten the keypress in the moment, but
the broken echo was sitting right there in plain text and I had skimmed past
it. That was its own little lesson in what I’m not noticing in terminal
output.
I checked echo $TERM on the Pi:
TERM=xterm-ghostty
There it was. I run Ghostty as my terminal emulator, and Ghostty
advertises itself with TERM=xterm-ghostty. SSH faithfully forwards that
value to the remote host. The remote host then asks the system “do you have
a terminfo entry named xterm-ghostty?” and Raspberry Pi OS Bookworm, like
most distros, does not. So less, vim, htop, even bash’s readline
all fall back to the most pessimistic assumption about what the terminal can
do. Hence the warning. Hence the raw escape bytes.
The fix
Ghostty’s docs cover this. The canonical fix is to copy your local terminfo entry to the remote host:
infocmp -x xterm-ghostty | ssh root@dns03 -- tic -x -
infocmp -x dumps the compiled terminfo source for the named entry, the
pipe sends it to the remote, and tic -x compiles and installs it into the
remote’s terminfo database. Run it once per host.
If you can’t SSH directly as root (I can’t), it’s a two-step:
# step 1: drop the source on the remote
infocmp -x xterm-ghostty | ssh user@dns03 'cat > /tmp/xterm-ghostty.ti'
# step 2: install it system-wide (needs a real TTY for sudo)
ssh -t user@dns03 'sudo tic -x /tmp/xterm-ghostty.ti && rm /tmp/xterm-ghostty.ti'
sudo tic -x writes to /etc/terminfo/x/xterm-ghostty, which means every
user on the host benefits, including whatever you do under sudo later.
Modern tic will print a warning that looks alarming but isn’t:
"/tmp/xterm-ghostty.ti", line 2, col 31, terminal 'xterm-ghostty':
older tic versions may treat the description field as an alias
That just means: the terminfo source uses a long description in the second
pipe-delimited field (xterm-ghostty|Ghostty|...), which an ancient tic
on some other host might misread. Your install on Bookworm is fine.
After that, systemctl status is clean, less is happy, vim has color,
and the up-arrow does what an up-arrow is supposed to do.
The plot twist: my UDM-Pro
A day later I SSH’d into my UDM-Pro for an unrelated reason. As soon as
I started typing, the shell felt off. Same old “off.” For years I have
had a bug on the UDM-Pro where I cannot use the left-arrow key to move the
cursor backwards over text I had already typed. If I made a typo near the
beginning of a long command, my only options were to retype it or backspace
the whole thing. Annoying, but I had a workaround: if I started a tmux
session locally first and then SSH’d from inside tmux, everything worked.
I never knew why that worked. I just did it.
This time, on muscle memory, I opened a tmux window to SSH from. And then
it hit me.
tmux re-exports TERM as tmux-256color (or screen-256color depending
on config), and every Linux distro on Earth ships terminfo for those.
So when I went through tmux, the UDM-Pro was getting a TERM it
recognized. When I SSH’d directly from Ghostty, it was getting
xterm-ghostty, which the UDM had never heard of, so bash’s readline fell
back to the most degraded line-editor mode it has, the one where the
left-arrow doesn’t reliably walk back over already-printed text. Same root
cause as the Pi. I had been working around it for years without ever
asking why the workaround worked.
I checked, and tic exists on UniFi OS (it’s Debian Bullseye underneath),
so the fix was the same one-liner:
infocmp -x xterm-ghostty | ssh [email protected] -- tic -x -
Reconnected. Arrow keys work. less is clean. vim has color and syntax
highlighting. A multi-year papercut, gone in one command.
One footnote for UniFi people: the UDM-Pro’s root filesystem is an overlay
that may get reset by firmware upgrades. /etc/terminfo has persisted
across reboots for me, but I’d expect to re-run this after a UniFi OS
upgrade.
What I actually learned
The thing I am embarrassed about is not that I didn’t know the fix. The thing I am embarrassed about is that I never really understood, at a gut level, how much of “does my shell feel right” is downstream of one environment variable and a lookup in a database I never thought about.
TERM is not a label. It is a key into terminfo, and terminfo is the
contract that tells every program (bash’s readline, less, vim, git,
htop, systemctl’s pager, every TUI you have ever used) what bytes to
send for “move cursor left,” what bytes mean “the user pressed up-arrow,”
whether color is safe, whether undercurl is supported, whether the terminal
has a status line. If the remote host can’t resolve TERM to an entry, all
of those programs fall back to the most paranoid assumptions they have, and
your shell starts behaving in subtly broken ways that you might just attribute
to “this host is weird.” For sixteen years I had been attributing it to “this
host is weird.”
It was never the host. It was always TERM.