Fixing the SSH `LC_CTYPE=UTF-8` locale warning on Debian/LMDE

· 3 min read

When SSH-ing from macOS into a fresh Debian/LMDE box, bash greets you with a repeated warning:

bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

Harmless, but noisy. Here’s what’s going on and how to fix it server-side.

Why it happens

macOS Terminal (and iTerm) forward locale env vars to the remote host by default, sending values like LC_CTYPE=UTF-8 or LC_ALL=UTF-8. That literal string UTF-8 is not a valid locale name — proper names look like en_US.UTF-8.

The server’s sshd accepts these via AcceptEnv LANG LC_* (default in Debian’s /etc/ssh/sshd_config). When bash starts and calls setlocale() with UTF-8, glibc cannot find a locale by that name and prints the warning. The shell keeps running with the fallback locale, so nothing actually breaks — but the warning fires once per setlocale() call during startup, hence the repetition.

Quick reproduction on the server itself:

$ env -i LC_ALL=UTF-8 bash -c 'echo hi'
bash: warning: setlocale: LC_ALL: cannot change locale (UTF-8): No such file or directory
hi

The fix (server side)

Tell glibc that UTF-8 is a valid locale name by generating it as an alias for en_US UTF-8:

sudo apt install -y locales locales-all
sudo localedef -i en_US -f UTF-8 UTF-8
sudo update-locale LANG=en_US.UTF-8

What each step does:

  • locales — provides locale-gen / localedef and the locale definition sources.
  • locales-all — pre-generates every locale glibc knows about, saves reconfigure round trips.
  • localedef -i en_US -f UTF-8 UTF-8 — the actual fix: builds a locale literally named UTF-8 from the en_US input file with UTF-8 charmap. This is the entry setlocale("UTF-8") was failing to find.
  • update-locale LANG=en_US.UTF-8 — ensures the system default is sane and written to /etc/default/locale.

Verifying

Confirm the new locale is registered:

$ locale -a | grep -i utf-8
UTF-8
en_US.utf8
...

Then reproduce the earlier failing command:

$ env -i LC_ALL=UTF-8 bash -c 'echo ok'
ok

No warning. Open a fresh SSH session from macOS — clean login banner.

Alternatives (not used here)

  • Client side: stop macOS Terminal from forwarding locale env vars (Terminal → Preferences → Profiles → Advanced → uncheck Set locale environment variables on startup), or add SendEnv -LC_* to ~/.ssh/config. Only fixes it for that one client.
  • Server side, blunter: comment out AcceptEnv LANG LC_* in /etc/ssh/sshd_config and reload sshd. Server then ignores whatever the client sends.

The localedef approach is the least invasive: fixes the actual root cause (missing locale definition), works for every client, no config file surgery.

Environment

  • Server: LMDE 7 (gigi), glibc 2.41.
  • Client: macOS Terminal (default profile forwards LC_*).