gitbutlerapp/gitbutler · error
product name not set
Error message
product name not set
What it means
get_server_addr() picks the devtools/tokio-console port (6667/6668/6669) by comparing app_handle.config().product_name against known names. Tauri's productName config field is optional; when the running app's Tauri context has no productName, config.product_name is None and expect("product name not set") panics inside logs::init.
Source
Thrown at crates/gitbutler-tauri/src/logs.rs:113
/// It is pretty much the same as `LevelFilter` in debug builds.
fn should_log(level: Option<Level>, meta: &tracing::Metadata<'_>) -> bool {
let Some(level) = level else {
return false;
};
if *meta.level() > level {
return false;
}
if level > Level::DEBUG {
return true;
}
meta.module_path().is_none_or(|p| {
p.starts_with("gitbutler_") || p.starts_with("but::") || p.starts_with("but_")
})
}
fn get_server_addr(app_handle: &AppHandle) -> (Ipv4Addr, u16) {
let config = app_handle.config();
let product_name = config.product_name.as_ref().expect("product name not set");
let port = if product_name.eq("GitButler") {
6667
} else if product_name.eq("GitButler Nightly") {
6668
} else {
6669
};
(Ipv4Addr::LOCALHOST, port)
}
/// Originally based on https://github.com/tokio-rs/tracing/blob/44861cad7a821f08b3c13aba14bb8ddf562b7053/tracing-appender/src/rolling.rs#L571
#[instrument(err(Debug))]
fn prune_old_logs(
log_directory: &Path,
log_filename_prefix: Option<&str>,
log_filename_suffix: Option<&str>,
max_files: usize,
) -> anyhow::Result<()> {View on GitHub (pinned to caf1f223d3)
Solutions
- Set productName in the Tauri config ("GitButler", "GitButler Nightly", "GitButler Test", or your fork's name)
- If the field is legitimately optional in your build, default it instead of expecting (see exampleFix)
- Debug-print app_handle.config().product_name at startup to confirm what the running context actually contains
Example fix
// before
let product_name = config.product_name.as_ref().expect("product name not set");
// after
let product_name = config.product_name.as_deref().unwrap_or("GitButler"); Defensive patterns
Strategy: validation
Validate before calling
// Before startup, confirm the packaged config carries productName
let cfg: tauri_utils::config::Config = serde_json::from_str(include_str!("../tauri.conf.json"))?;
assert!(cfg.product_name.is_some(), "productName missing in tauri.conf.json"); Type guard
fn has_product_name(app: &tauri::AppHandle) -> bool {
app.config().product_name.is_some()
} Prevention
- Always set productName in tauri.conf.json, including forks and test apps
- After Tauri upgrades, re-check config schema - optional fields stay optional and code must not assume them
- Default unknown product names gracefully instead of expecting, so channel variants don't crash
When it happens
Trigger: Any startup path reaching get_server_addr (performance/tokio-console logging setup) in a build whose Tauri configuration lacks productName: custom forks trimming tauri.conf.json, test apps, or contexts generated without the field.
Common situations: Forking the desktop app and slimming tauri.conf.json, E2E/test builds using a different config, or Tauri version upgrades that move/rename the productName field.
Related errors
- failed to create logs dir
- initializing rolling file appender failed
- failed to create config dir
- failed to set subscriber
- missing config dir
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/802906782e0754ab.
Report an issue: GitHub.