aaif-goose/goose · error
Failed to set global subscriber: {}
Error message
Failed to set global subscriber: {} What it means
goose-cli sets up its file-only JSON tracing subscriber with SubscriberInitExt::try_init inside a OnceLock (crates/goose-cli/src/logging.rs:24-26). try_init returns Err when some other component has already installed a global default tracing subscriber in this process, because Rust allows only one global default subscriber. The OnceLock only guards goose's own repeat calls, not a subscriber installed by another crate or an earlier init in the same process.
Source
Thrown at crates/goose-cli/src/logging.rs:26
/// Sets up the logging infrastructure for the CLI.
/// Logs go to a JSON file only (no console output).
pub fn setup_logging(name: Option<&str>) -> &'static Result<()> {
INIT.get_or_init(|| {
use tracing_subscriber::util::SubscriberInitExt;
init_goose_request_log()?;
let config = goose::logging::LoggingConfig {
component: "cli",
name,
extra_directives: &["goose_cli=info"],
console: false,
json: true,
};
let subscriber = goose::logging::build_logging_subscriber(&config)?;
subscriber
.try_init()
.map_err(|e| anyhow::anyhow!("Failed to set global subscriber: {}", e))?;
Ok(())
})
}
#[cfg(test)]
mod tests {
use goose::tracing::langfuse_layer;
use std::env;
use tempfile::TempDir;
fn setup_temp_home() -> TempDir {
let temp_dir = TempDir::new().unwrap();
if cfg!(windows) {
env::set_var("USERPROFILE", temp_dir.path());
} else {
env::set_var("HOME", temp_dir.path());
}
temp_dirView on GitHub (pinned to 3810898a74)
Solutions
- Find which component installed a global subscriber first (search the process init path for set_global_default/try_init) and let goose's setup_logging run before it, or remove the duplicate init
- If double-init is expected and harmless, treat the 'already initialized' failure as Ok instead of an error
- In tests, use per-test capturing subscribers (e.g. tracing_test or a subscriber-scoped guard) instead of the global default
Example fix
// before
subscriber
.try_init()
.map_err(|e| anyhow::anyhow!("Failed to set global subscriber: {}", e))?;
// after (tolerate an already-installed global subscriber)
use tracing_subscriber::util::SubscriberInitExt;
let _ = subscriber.try_init(); // logs nothing when the global default is already set Defensive patterns
Strategy: try-catch
Try / catch
// Rust: tolerate a subscriber that was already installed globally
use tracing_subscriber::util::SubscriberInitExt;
match subscriber.try_init() {
Ok(()) => {}
Err(e) if e.to_string().contains("already set") => {
// Another component owns the global default; keep logging via its subscriber
tracing::debug!("global tracing subscriber already set: {e}");
}
Err(e) => return Err(anyhow::anyhow!("Failed to set global subscriber: {e}")),
} Prevention
- Initialize tracing exactly once per process, before any dependency that may install its own subscriber
- In test suites, prefer per-test capturing subscribers over global defaults
- Grep dependency init paths for set_global_default/try_init when embedding goose-cli code
When it happens
Trigger: Calling setup_logging after another crate (a test harness, an embedded tracing setup, or a library calling tracing::subscriber::set_global_default / try_init first) already set the global default; also any second, differently-configured init path that bypasses the OnceLock.
Common situations: Running goose-cli code inside test binaries that install their own subscriber (e.g. tracing-subscriber's env-filter init in tests), embedding goose-cli functions in another binary that logs first, or a dependency (telemetry/otel) that initializes tracing during its own setup.
Related errors
- Failed to init llama backend: {}
- Provider not set
- state-machine session loaded without conversation
- state machine conversation has no kickoff message
- cannot determine the role of an empty conversation
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/58f153184dce46c0.
Report an issue: GitHub.