gitbutlerapp/gitbutler · info

symbols must always be initialized

Error message

symbols must always be initialized

What it means

Theme::sym() unwraps the private field symbols: Option<ThemeSymbols>. Every sanctioned constructor populates it: default_for() assigns t.symbols = Some(ThemeSymbols::new(&t)), Default delegates there, and the field is #[serde(skip_serializing, skip_deserializing)] while the container-level #[serde(default)] restores it from Theme::default(), so JSON-loaded themes also carry Some. The expect documents an internal invariant: a panic means a Theme was constructed bypassing these constructors.

Solutions

  1. Construct themes only via Theme::default(), Theme::default_for(preset), or serde deserialization
  2. When adding a constructor, finish it with t.symbols = Some(ThemeSymbols::new(&t)) exactly like default_for does
  3. Long term: apply the refactor noted in the source comment - split symbols out of the deserialized struct so the Option disappears

Example fix

// before
let mut t = default_dark();
// ... symbols still None here -> t.sym() would panic

// after
let mut t = default_dark();
t.symbols = Some(ThemeSymbols::new(&t)); // invariant restored before sym() is reachable
Defensive patterns

Strategy: validation

Validate before calling

// Only sanctioned constructors keep the invariant; route all creation through them:
let theme = but::theme::Theme::default_for(preset); // symbols initialized
// or deserialize - container-level #[serde(default)] restores symbols
let theme: but::theme::Theme = serde_json::from_str(json)?;

Prevention

When it happens

Trigger: Only in-crate construction can leave symbols None - e.g. using default_light()/default_dark() output before default_for() fixes it up, or new code building/rebuilding a Theme struct literal that drops the field. Calling .sym() on such a value panics.

Common situations: Contributors adding new Theme constructors or partially cloning/rebuilding Theme values. Not triggerable by user theme files: serde round-trips keep symbols initialized.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/da4d146463590174. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/theme.rs:392

/// Helper — builds a bold + colored [`Style`].
const fn style_fg_bold(fg: Color) -> Style {
    Style::new().fg(fg).add_modifier(Modifier::BOLD)
}

impl Default for Theme {
    /// Produces the canonical color palette.
    fn default() -> Self {
        Self::default_for(ThemePreset::Dark)
    }
}

impl Theme {
    /// Get the symbols for this theme.
    pub fn sym(&self) -> &ThemeSymbols {
        self.symbols
            .as_ref()
            .expect("symbols must always be initialized")
    }

    /// Produces a specific default color palette.
    pub fn default_for(preset: ThemePreset) -> Self {
        let mut t = match preset {
            ThemePreset::Light => Self::default_light(),
            ThemePreset::Dark => Self::default_dark(),
        };
        t.symbols = Some(ThemeSymbols::new(&t));
        t
    }

    /// Load the syntax highlighting theme.
    pub fn load_syntax_highlighting_theme(&self) -> anyhow::Result<highlighting::Theme> {
        Ok(ThemeSet::load_from_reader(&mut std::io::Cursor::new(
            self.syntax_highlighting_theme_raw,
        ))?)
    }

View on GitHub (pinned to caf1f223d3)