Linux GUI never starts: locale query returns "C" (setlocale never initialized) makes compose-table acquisition fatal
What Operating System(s) are you seeing the problem on?
Linux
Which Wayland compositor / X11 WM (if applicable)
COSMIC (Pop!_OS 24.04) — reproduces on both Wayland and X11 paths
WezTerm version
wezterm 20260829-194257-08e5e0af (built from source, HEAD 08e5e0a)
Did you try this in a new, clean WezTerm config file?
- Yes, I tried a new, clean config file
Describe the bug
On a fresh session the GUI never starts — both display paths die during keyboard init:
Wayland:
xkbcommon: ERROR: couldn't find a Compose file for locale "C" (mapped to "C")
ERROR window::os::wayland::keyboard > Error processing keymap change: Failed to acquire compose table from locale
ERROR env_bootstrap > panic at window/src/os/wayland/keyboard.rs:113:38 - !?
thread 'main' panicked ... no keymapX11:
xkbcommon: ERROR: couldn't find a Compose file for locale "C" (mapped to "C")
ERROR wezterm_gui > Failed to acquire compose table from locale; terminatingRoot cause
query_lc_ctype() in window/src/os/x11/keyboard.rs calls libc::setlocale(libc::LC_CTYPE, NULL) — a query-only call. Rust programs never call setlocale(..., "") at startup, so the C runtime locale is still the default "C", and the query always returns "C" regardless of $LANG/$LC_CTYPE. Table::new_from_locale then fails for "C", which is fatal (wayland: keymap mapper None → expect("no keymap") panic; X11: terminating).
The macOS-only env-bootstrap locale probe exists, but the Linux path has no equivalent initialization.
Two-layer reproduction note (Pop!_OS 24.04): even with the locale query fixed, xkbcommon's compose.dir lookup for en_US.UTF-8 fails on this system (the Compose file exists at /usr/share/X11/locale/en_US.UTF-8/Compose), so a fallback to a direct file load is also needed.
Fix (both applied locally, builds and runs green on Wayland/WebGpu)
1. Initialize the locale from the environment before querying (window/src/os/x11/keyboard.rs):
fn query_lc_ctype() -> anyhow::Result<&'static OsStr> {
// Rust never calls setlocale; a NULL query always returned "C".
unsafe { libc::setlocale(libc::LC_CTYPE, b"\0".as_ptr().cast()) };
let ptr = unsafe { libc::setlocale(libc::LC_CTYPE, std::ptr::null()) };
ensure!(!ptr.is_null(), "failed to query locale");
let cstr = unsafe { CStr::from_ptr(ptr) };
Ok(OsStr::from_bytes(cstr.to_bytes()))
}2. Compose-table fallback chain at the three new_from_locale sites in the same file: on failure, retry via Table::new_from_buffer with the direct /usr/share/X11/locale/en_US.UTF-8/Compose bytes, then /usr/share/X11/locale/C/Compose (empty-but-valid on some distros) before giving up:
let table = xkb::compose::Table::new_from_locale(&context, locale, xkb::compose::COMPILE_NO_FLAGS)
.or_else(|_| {
let bytes = std::fs::read("/usr/share/X11/locale/en_US.UTF-8/Compose")
.map_err(|e| anyhow!("compose file read failed: {e}"))?;
xkb::compose::Table::new_from_buffer(&context, bytes, "en_US.UTF-8",
xkb::compose::FORMAT_TEXT_V1, xkb::compose::COMPILE_NO_FLAGS)
.map_err(|_| anyhow!("direct Compose file parse failed"))
})
.or_else(|_| {
let bytes = std::fs::read("/usr/share/X11/locale/C/Compose")
.map_err(|e| anyhow!("C compose read failed: {e}"))?;
xkb::compose::Table::new_from_buffer(&context, bytes, "C",
xkb::compose::FORMAT_TEXT_V1, xkb::compose::COMPILE_NO_FLAGS)
.map_err(|_| anyhow!("C Compose parse failed"))
})
.map_err(|_| anyhow!("Failed to acquire compose table from locale"))?;After both patches the GUI starts cleanly under Wayland (WebGpu) and X11 on this box.
Related: #6888 (same terminal state on Fedora; the dead_hamza Compose errors there are the second layer of this).
Source: wezterm/wezterm