lsd-rs/lsd · critical

Failed to read both config file and default config

Error message

Failed to read both config file and default config

What it means

Config::default() tries the user config file (from_file on the located config path), then falls back to the compiled-in DEFAULT_CONFIG YAML, and finally .expect()s that at least one succeeded. This panic fires only when both the user's config file fails to parse AND the built-in default config fails to parse — which in practice means the embedded DEFAULT_CONFIG string is malformed (a build/embedding bug), not something a normal user causes.

Source

Thrown at src/config_file.rs:212

impl Default for Config {
    /// Try to find either config.yaml or config.yml in the config directories
    /// and use the first one that is found. If none are found, or the parsing fails,
    /// use the default from DEFAULT_CONFIG.
    fn default() -> Self {
        Config::config_paths()
            .find_map(|p| {
                let yaml = p.join("config.yaml");
                let yml = p.join("config.yml");
                if yaml.is_file() {
                    Config::from_file(yaml)
                } else if yml.is_file() {
                    Config::from_file(yml)
                } else {
                    None
                }
            })
            .or(Self::from_yaml(DEFAULT_CONFIG).ok())
            .expect("Failed to read both config file and default config")
    }
}

pub const DEFAULT_CONFIG: &str = r#"---
# == Classic ==
# This is a shorthand to override some of the options to be backwards compatible
# with `ls`. It affects the "color"->"when", "sorting"->"dir-grouping", "date"
# and "icons"->"when" options.
# Possible values: false, true
classic: false

# == Blocks ==
# This specifies the columns and their order when using the long and the tree
# layout.
# Possible values: permission, user, group, context, size, date, name, inode, links, git
blocks:
  - permission
  - user

View on GitHub (pinned to 4b6c14a110)

Solutions

  1. Inspect and fix the DEFAULT_CONFIG constant in src/config_file.rs so it is valid YAML that deserializes into Config (watch duplicate '---' document separators).
  2. Reproduce from_yaml(DEFAULT_CONFIG) in a test to see the serde error and fix the struct/field mismatches.
  3. If a user config file is at fault, fix or remove it (default config locations such as ~/.config/eza/config.yml) or run with --ignore-config.
  4. Upgrade/rebuild eza if running a distro build with a broken embedded default.

Example fix

// before
pub const DEFAULT_CONFIG: &str = r#"---
# == Classic ==
---
..."#; // stray second '---' can split/invalid documents
// after
pub const DEFAULT_CONFIG: &str = r#"# == Classic ==
..."#;
Defensive patterns

Strategy: fallback

Validate before calling

// sanity-check embedded defaults before release
let cfg: Config = serde_yaml::from_str(DEFAULT_CONFIG)
    .expect("DEFAULT_CONFIG must parse");

Type guard

fn valid_user_config(s: &str) -> bool {
    serde_yaml::from_str::<Config>(s).is_ok()
}

Try / catch

let config = Config::default(); // panics by design
// wrap at process boundary:
let result = std::panic::catch_unwind(|| Config::default());
let config = result.unwrap_or_else(|_| Config::with_none());

Prevention

When it happens

Trigger: Calling Config::default() (via eza startup without --ignore-config) when from_file returns None (missing/unreadable config or parse error) and from_yaml(DEFAULT_CONFIG) returns Err because the embedded YAML does not deserialize into Config.

Common situations: A code change or merge corrupted the DEFAULT_CONFIG constant (note the double '---' document separator in the source, an easy YAML mistake); a serde rename/type change made the embedded defaults no longer deserialize; a user config with syntax errors combined with such a regression.

Related errors


AI-assisted analysis of lsd-rs/lsd@4b6c14a110 (2026-09-04). Data as JSON: /api/errors/e00b666f3485fae4. Report an issue: GitHub.