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
- userView on GitHub (pinned to 4b6c14a110)
Solutions
- 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).
- Reproduce from_yaml(DEFAULT_CONFIG) in a test to see the serde error and fix the struct/field mismatches.
- 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.
- 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
- Add a unit test that deserializes DEFAULT_CONFIG on every build.
- Keep one YAML document (avoid stray '---' separators) in the embedded default.
- Run eza with --ignore-config when debugging a broken user config.
- After upgrading, diff your config keys against the new version's documented format.
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.