Skip to content

Partial TOML Sub-Config Sections Silently Fall Back to Full Defaults #1482

@Jitsusama

Description

@Jitsusama

Problem

Five sub-config structs in src/core/config.rs don't have #[serde(default)]: TrackingConfig, DisplayConfig, FilterConfig, TelemetryConfig and LimitsConfig. When a TOML section omits any field, toml::from_str returns a missing field error. That error bubbles up to Config::load, and every helper that consumes the config (for example config::limits) calls .unwrap_or_default() on the Result, which silently swaps in the full default config and discards every user override in that section.

There's no feedback to the user. rtk config shows all defaults, so it looks like the file was parsed correctly when it wasn't.

Steps To Reproduce

# ~/.config/rtk/config.toml
[limits]
grep_max_results = 500

Expected: grep_max_results is 500, other limits stay at their documented defaults.

Actual: the whole [limits] section fails to parse (missing field grep_max_per_file), Config::load returns an error, and config::limits() silently falls back to LimitsConfig::default(). grep_max_results is 200.

Same pattern affects every other sub-config section: [tracking] requires history_days, [display] requires colors/emoji/max_width, [filters] requires both fields, and [telemetry] requires enabled. Omitting any of them breaks the whole section silently.

Impact

Users who override a single field in any section get no signal that their setting was ignored. The first time I noticed this was a partial [limits] section in my dotfiles that I assumed was working for months.

This looks like the bug that was reported in #1134 (closed as user confusion about [telemetry] vs [tracking]) and attempted in #1135 (self-closed for wrong base branch). The underlying parsing bug was never fixed.

Proposed Fix

Add struct-level #[serde(default)] to each of the five sub-config structs. They all already implement Default with the correct domain values, so the attribute just delegates to what's already there. No logic changes, no new defaults to maintain, backward compatible with fully specified configs.

#[derive(Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct LimitsConfig { ... }

Relevant Code

  • src/core/config.rs:34-126: the five sub-config struct definitions.
  • src/core/config.rs:128-130: config::limits() shows the silent-fallback pattern (Config::load().map(|c| c.limits).unwrap_or_default()).
  • HooksConfig at src/core/config.rs:27-31 already uses field-level #[serde(default)], so the pattern is established.

Co-Authored-By AI (Claude Opus 4.7) via Pi

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeffort-smallQuelques heures, 1 fichiergood first issueGood for newcomers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions