libnyanpasu/clash-nyanpasu · info

`en-US` is a valid BCP47 language tag

Error message

`en-US` is a valid BCP47 language tag

What it means

locale_or_fallback converts a system locale string into a LanguageTag, defensively parsing and canonicalizing because sys-locale output isn't guaranteed valid. If parsing fails (or input is None), it falls back to a hardcoded "en-US" whose parse is asserted with .expect — the panic fires only if that constant stops parsing, i.e. a broken regex/parser or edited constant.

Source

Thrown at backend/nyanpasu-helper/src/locale.rs:29

}

/// Parse and canonicalize a raw locale string, falling back to `en-US` when it
/// is absent or not a valid BCP47 language tag.
///
// FIXME(sys-locale): `sys_locale::get_locale` documents that it returns a
// BCP47 tag, but its Unix backend does not enforce this. It merely transforms
// the `LANG` / `LC_*` environment variables (stripping `.<codeset>` / `@<mod>`
// and turning `_` into `-`) without validating the grammar or filtering the
// POSIX `C` / `POSIX` locales. On minimal Linux/CI environments where the
// locale is `C` / `C.UTF-8`, it returns `Some("C")` — a single-letter primary
// subtag that is *not* a valid BCP47 language tag — instead of `None`. We must
// therefore parse defensively here rather than trusting the documented
// contract. Revisit if a future `sys-locale` release returns `None` for the
// POSIX locale.
fn locale_or_fallback(raw: Option<&str>) -> LanguageTag {
    raw.and_then(|raw| LanguageTag::parse(raw).ok()?.canonicalize().ok())
        .unwrap_or_else(|| {
            LanguageTag::parse("en-US").expect("`en-US` is a valid BCP47 language tag")
        })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_system_locale() {
        // Must never panic regardless of the host locale.
        let locale = get_system_locale();
        eprintln!("System locale: {}", locale);
    }

    #[test]
    fn invalid_or_absent_locale_falls_back_to_en_us() {
        let fallback = LanguageTag::parse("en-US").unwrap();
        // The `C` locale (common in CI) has a single-letter primary subtag,

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Do not modify the "en-US" fallback constant to a non-BCP47 value.
  2. If you need a different default, verify it parses with LanguageTag::parse before committing.
  3. Check for vendored/broken versions of the language-tags crate if this panics.
  4. Add a unit test asserting the fallback parses, so a bad edit fails at test time instead of runtime.

Example fix

// before: invalid fallback edit
LanguageTag::parse("en_US").expect("`en-US` is a valid BCP47 language tag")

// after: keep a valid BCP47 tag
LanguageTag::parse("en-US").expect("`en-US` is a valid BCP47 language tag")
Defensive patterns

Strategy: validation

Validate before calling

// Verify any custom fallback parses at startup
fn valid_fallback(tag: &str) -> bool { LanguageTag::parse(tag).is_ok() }
assert!(valid_fallback("en-US"));

Try / catch

catch_unwind around locale resolution; on panic, fall back to a raw string locale.

Prevention

When it happens

Trigger: Triggered only when LanguageTag::parse("en-US") fails — practically impossible unless the language-tags crate is broken, the constant was edited to an invalid tag, or parsing is misconfigured (e.g. custom parser rejecting valid tags).

Common situations: Not hit in real deployments. Developers encounter it conceptually when changing the fallback constant or vendoring an incompatible version of the parsing crate.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/ac38effd12606548. Report an issue: GitHub.