gitbutlerapp/gitbutler · error
symbols must always be initialized
Error message
symbols must always be initialized
What it means
Theme::sym() returns the derived ThemeSymbols stored as an Option field, because symbols can only be constructed from the fully-built theme (default_for sets t.symbols = Some(ThemeSymbols::new(&t)) last). The field is #[serde(skip_serializing, skip_deserializing)] (theme.rs:356-363), so any Theme produced by deserialization — notably theme::load() reading a custom JSON theme file — has symbols == None, and the first sym() call panics. Every construction path that bypasses default_for (serde, struct literals) hits this.
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 2497b8007a)
Solutions
- Finish deserialized themes: in theme::load(), after serde_json::from_str, set theme.symbols = Some(ThemeSymbols::new(&theme)) (or add a pub fn finish() and call it wherever a Theme is built)
- If calling from outside the crate, construct via Theme::default_for(preset) and overlay JSON fields instead of deserializing a full Theme
- Make sym() degrade to a static default symbol table instead of expecting
- Add a regression test: load a JSON theme, then call sym()
Example fix
// before (theme::load) let theme: Theme = serde_json::from_str(&contents)?; Ok(theme) // after — complete post-deserialization initialization let mut theme: Theme = serde_json::from_str(&contents)?; theme.symbols = Some(ThemeSymbols::new(&theme)); Ok(theme)
Defensive patterns
Strategy: try-catch
Validate before calling
// probe a custom-theme-loaded Theme before relying on it in a host process
let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| theme.sym())).is_ok();
if !ok { /* fall back to Theme::default_for(ThemePreset::Dark) */ } Try / catch
// degrade to the default palette's symbols instead of crashing the TUI
let theme = std::panic::catch_unwind(AssertUnwindSafe(|| theme::load(path)))
.ok()
.map(|mut t| { t.symbols = Some(ThemeSymbols::new(&t)); t }) // when accessible
.unwrap_or_else(Theme::default); Prevention
- Construct themes via Theme::default_for(preset); it performs the mandatory symbols post-init step
- Any Theme that round-tripped through serde (theme::load, from_str) needs symbols re-initialized before sym() is called — pair load() with that step
- Add a regression test: load a JSON theme then call sym()
- Prefer fixing load() to finish initialization rather than guarding every sym() call site
When it happens
Trigger: Loading a custom theme from a JSON file via theme::load() (theme.rs:93-97 returns the deserialized value without filling symbols) and then rendering anything that calls Theme::sym() — TUI symbols/icons on first draw. Also manually constructing a Theme literal in code without the final symbols step.
Common situations: A user-supplied theme file configured through but's config; tests/tools building a Theme via serde_json::from_*; partial theme JSON (absent fields keep defaults, but symbols is skipped entirely so it stays None regardless of file contents).
Related errors
- line should have been highlighted by now
- no searchable columns
- FuzzyPickerItem::columns must return a searchable column
- MCP Apps capability is an object
- theme may only be initialized once
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/da4d146463590174.
Report an issue: GitHub.