Fixing "cannot set LC_ALL" Locale Warnings
What You'll Learn
- Why the
cannot set LC_ALL/setlocalewarning 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 withlocale -a - If it's missing, generate it with
locale-gen; if you're in a hurry, silence it withexport 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_ALL → LC_* → 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
localespackage itself is not installed (minimal containers, cloud images) - The package is present but the locale was never generated (
locale-gennot run) - SSH forwarded your terminal's locale and the target server lacks the same one
- A typo (
en_US.utf8vsen_US.UTF-8, etc.) points at a name that doesn't exist
How do I find which variable is wrong?
Conclusion: Use
localefor the current values and the offending variable, andlocale -afor 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 viaAcceptEnv. 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
localespackage, generate the target locale withlocale-gen, and set the default withupdate-locale. The interactivedpkg-reconfigure localesdoes 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 withlocalectl 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=Corexport LANG=C.UTF-8to 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_ALLpermanently, 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