Fixing "cannot set LC_ALL" Locale Warnings

Fixing "cannot set LC_ALL" Locale Warnings

What You'll Learn

  • Why the cannot set LC_ALL / setlocale warning appears
  • How to pin down the offending variable with locale / locale -a
  • How to silence it quickly and how to generate the locale for a permanent fix

Conclusion (triage pattern)

  • The warning means one thing: the requested locale does not exist on the system
  • First check the warning with locale, then the locales you actually have with locale -a
  • If it's missing, generate it with locale-gen; if you're in a hurry, silence it with export LC_ALL=C

Assumptions (target environment)

  • OS: Ubuntu / Debian / RHEL family (glibc with systemd)
  • Can happen on a local terminal, an SSH target server, or a container

Why does the "cannot set LC_ALL" warning appear?

Conclusion: Because the locale your LANG / LC_* variables request (e.g. en_US.UTF-8) is not generated or installed on that system. Asked for a locale that doesn't exist, glibc falls back to the C locale.

A locale bundles language, character encoding, and date/number formatting under a name like en_US.UTF-8 or ja_JP.UTF-8. On startup a program calls setlocale() and resolves the requested locale in priority order: LC_ALLLC_*LANG.

When the requested name does not exist on the system, glibc emits a warning and falls back to the safe C (POSIX) locale. Typical messages:

# the locale command itself
locale: Cannot set LC_ALL to default locale: No such file or directory

# bash / various commands
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory

# perl (common in apt and Git hooks)
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LC_ALL = (unset),
	LC_CTYPE = "UTF-8",
	LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

Main "doesn't exist" patterns

  • The locales package itself is not installed (minimal containers, cloud images)
  • The package is present but the locale was never generated (locale-gen not run)
  • SSH forwarded your terminal's locale and the target server lacks the same one
  • A typo (en_US.utf8 vs en_US.UTF-8, etc.) points at a name that doesn't exist

How do I find which variable is wrong?

Conclusion: Use locale for the current values and the offending variable, and locale -a for the list of available locales. If the requested name isn't in the list, that's the cause.

Running locale shows the current settings and which variable is the problem at once.

$ locale
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
...
LC_ALL=

Next, list the locales the system can actually use.

$ locale -a
C
C.UTF-8
POSIX

Here en_US.UTF-8 is requested, but the list only has C-family locales. The requested name not appearing in locale -a is the decisive evidence.

Watch for naming variations. locale -a may print the en_US.utf8 form (lowercase, no hyphen), but a LANG=en_US.UTF-8 setting is treated as the same. However, en_US (no encoding) and en_US.UTF-8 are different, so check that the suffix matches too.

Why does the warning appear over SSH?

Conclusion: The SSH client forwards your local locale with SendEnv LANG LC_*, and the server accepts it via AcceptEnv. If the server doesn't have that locale, you get the warning on every login.

If it never shows locally but appears every time you SSH to a server, this is almost always the cause. Most distros ship an SSH client config with this line:

# /etc/ssh/ssh_config or ~/.ssh/config
SendEnv LANG LC_*

This sends your LANG=en_US.UTF-8 (and friends) to the server. The server's /etc/ssh/sshd_config AcceptEnv LANG LC_* receives them and sets the session environment. If the server lacks that locale, setlocale fails when the shell starts.

You have two options: install the locale on the server (steps below) or stop forwarding it. To stop forwarding, edit the client config.

# Disable forwarding for one host in ~/.ssh/config
Host myserver
    SendEnv -LANG -LC_*

To suppress forwarding for a single connection, override it with ssh -o.

$ ssh -o SendEnv= user@myserver

How do I generate or install the locale? (Debian / Ubuntu)

Conclusion: Install the locales package, generate the target locale with locale-gen, and set the default with update-locale. The interactive dpkg-reconfigure locales does the same.

On Debian / Ubuntu, a locale is two steps: install the package and generate it.

# 1. The locales package (for minimal systems without it)
$ sudo apt update && sudo apt install -y locales

# 2. Generate the target locales
$ sudo locale-gen en_US.UTF-8 ja_JP.UTF-8

# 3. Set the system default locale
$ sudo update-locale LANG=en_US.UTF-8

Re-check with locale -a that it was generated. The persistent setting lives in /etc/default/locale.

$ cat /etc/default/locale
LANG=en_US.UTF-8

To pick from a menu instead, use the command below and select locales with the space bar to generate them.

$ sudo dpkg-reconfigure locales

Changes from update-locale and /etc/default/locale take effect from a new login session. To apply them to the current shell immediately, log out and back in, or set a value for now with export LANG=en_US.UTF-8.

Fixing it on RHEL / Fedora

Conclusion: On RHEL 8 and later, install the locale via the glibc-langpack-<lang> package and set the default with localectl set-locale.

On RHEL / CentOS Stream / Fedora, you install a per-language langpack.

# The English locale set
$ sudo dnf install -y glibc-langpack-en

# Add Japanese if you need it
$ sudo dnf install -y glibc-langpack-ja

# Set the system default
$ sudo localectl set-locale LANG=en_US.UTF-8

localectl is the common locale-management command on systemd systems, and it writes the setting to /etc/locale.conf. Check the current value with localectl status.

$ localectl status
   System Locale: LANG=en_US.UTF-8
       VC Keymap: us
      X11 Layout: us

On a Debian-family system that also has localectl (systemd), you can set the default with localectl set-locale too. But generating the locale is still locale-gen's job, so the generate → localectl order stays the same.

A quick workaround when you just want it quiet

Conclusion: Use export LC_ALL=C or export LANG=C.UTF-8 to switch to a locale that is guaranteed to exist. It's not a real fix, but it stops the warning instantly.

For a script run or a one-off task where you "just want the warning gone right now," point at a C-family locale that is certain to exist.

# Silence it completely (English messages, ASCII collation)
$ export LC_ALL=C

# Keep UTF-8 (available on most systems)
$ export LANG=C.UTF-8
$ unset LC_ALL

C (= POSIX) and C.UTF-8 are built into glibc and work almost everywhere without locale-gen. Choosing C.UTF-8 keeps UTF-8 byte sequences intact while using English messages.

LC_ALL is the highest-priority variable that overrides every LC_*. Pinning it to C forces character classification and sort order to the C locale too. Putting LC_ALL=C in a permanent file (like .bashrc) invites other bugs, so keep it a workaround and fix it permanently by generating the locale.

If you only want to quiet a single script, scope it at the top.

#!/usr/bin/env bash
export LC_ALL=C.UTF-8

A checklist to prevent recurrence

Conclusion: Pre-generate the locales a server needs, stop unnecessary SSH forwarding, and avoid pinning LC_ALL permanently, and locale warnings are largely preventable.

Measure Command / setting Effect
Generate needed locales sudo locale-gen en_US.UTF-8 Removes the missing requested name
Set an explicit system default update-locale / localectl set-locale Pins the per-login default
Stop unneeded SSH forwarding SendEnv -LANG -LC_* Prevents importing missing locales
Keep workarounds local export LC_ALL=C.UTF-8 inside a script Avoids side effects of a global pin

Copy-paste: locale diagnosis one-liner

# Compare the requested locale against what's available
echo "--- requested ---"; locale 2>&1 | grep -E '^(LANG|LC_ALL|LC_CTYPE)='; \
  echo "--- available ---"; locale -a

Next Reading