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_dir

View on GitHub (pinned to 3810898a74)

Solutions

  1. 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
  2. If double-init is expected and harmless, treat the 'already initialized' failure as Ok instead of an error
  3. 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

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


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/58f153184dce46c0. Report an issue: GitHub.