gitbutlerapp/gitbutler · error
theme::init() must be called before getting the theme
Error message
theme::init() must be called before getting the theme
What it means
theme::get() reads the global OnceLock; in non-test builds THEME.get() returns None until theme::init() has run, so get() panics with 'theme::init() must be called before getting the theme'. Under cfg(test) the code falls back to get_or_init(Theme::default), which is why tests never hit it but real binaries do.
Solutions
- Call but::theme::init(...) at the very top of the binary's entry point, before command dispatch
- Use the panic backtrace to find the first theme::get() caller and move initialization ahead of it
- If ordering cannot be guaranteed, mirror the test behavior with get_or_init(Theme::default) (see exampleFix)
Example fix
// before
THEME.get().expect("theme::init() must be called before getting the theme")
// after
THEME.get_or_init(Theme::default) // safe fallback; init() still wins when it runs first Defensive patterns
Strategy: validation
Validate before calling
// In a new binary linking the but crate, initialize before any output:
fn main() {
but::theme::init(but::theme::Theme::default()); // first statement, before dispatch
// ... run commands ...
} Prevention
- Call theme::init() as the first statement of every binary entry point
- Remember cfg(test) masks this bug: tests default the theme, release binaries panic
- When adding early diagnostics output, either skip themed painting or init the theme first
When it happens
Trigger: Any rendering path that calls theme::get() (nearly every CLI output paints through it) executing before init() in a non-test binary: a new entry point, an example binary, a plugin that constructs but commands directly, or startup code reordered so a helper renders before initialization.
Common situations: Adding a new binary that links the but crate but skips the theme-init step; refactoring main so an early log/paint call runs before init; library consumers calling into but internals directly.
Related errors
- failed to set subscriber
- theme may only be initialized once
- broker already configured
- broker has not been configured
- Failed to build tauri app
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/60a0b8e4204f88e3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but/src/theme.rs:84
pub fn init(theme: Theme) {
THEME
.set(theme)
.expect("theme may only be initialized once");
}
/// Return a reference to the global theme.
///
/// Panics if [`init`] has not been called yet.
pub fn get() -> &'static Theme {
#[cfg(test)]
{
THEME.get_or_init(Theme::default)
}
#[cfg(not(test))]
{
THEME
.get()
.expect("theme::init() must be called before getting the theme")
}
}
/// Load a theme from a JSON file.
///
/// Fields that are absent in the file keep their [`Theme::default`] values.
pub fn load(path: &Path) -> anyhow::Result<Theme> {
let contents = std::fs::read_to_string(path)?;
let theme: Theme = serde_json::from_str(&contents)?;
Ok(theme)
}
/// Extension trait that lets us apply a [`Style`] to "paint" a string with raw ANSI escape codes.
///
/// ```ignore
/// use crate::theme::Paint;
/// let t = crate::theme::get();
/// writeln!(out, "{}", t.local_branch.paint(&name))?;View on GitHub (pinned to caf1f223d3)