#8119·wezterm

Linux GUI never starts: locale query returns "C" (setlocale never initialized) makes compose-table acquisition fatal

Author: ourocoCreated Aug 31, 2026Updated Sep 12, 2026
Labelsbugvariant: X11variant: WaylandPR welcome!T: keyboardai-assistedcrash/stuck

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 keymap

X11:

xkbcommon: ERROR: couldn't find a Compose file for locale "C" (mapped to "C")
ERROR wezterm_gui > Failed to acquire compose table from locale; terminating

Root 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 Noneexpect("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):

rust
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:

rust
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).