denoland/deno · error

failed to set otel globals

Error message

failed to set otel globals

What it means

deno_telemetry::init installs process-wide state (OtelGlobals) into a OnceCell. If the cell is already occupied — init ran twice in the same process — set() returns the previous value and this generic error is raised. It is an internal invariant violation, not a user configuration problem.

Source

Thrown at ext/telemetry/lib.rs:1423

  let span_attribute_count_limit = span_attribute_count_limit_from_env(sys);
  let span_attribute_value_length_limit =
    span_attribute_value_length_limit_from_env(sys);
  let sampler = Sampler::from_env(sys)?;

  OTEL_GLOBALS
    .set(OtelGlobals {
      log_processor,
      span_processor,
      id_generator,
      meter_provider,
      builtin_instrumentation_scope,
      span_event_count_limit,
      span_attribute_count_limit,
      span_attribute_value_length_limit,
      sampler,
      config,
    })
    .map_err(|_| deno_core::anyhow::anyhow!("failed to set otel globals"))?;

  deno_signals::before_exit(before_exit);
  deno_net::tunnel::disable_before_exit();

  Ok(())
}

fn before_exit() {
  log::trace!("deno_telemetry::before_exit");

  let Some(OtelGlobals {
    span_processor: spans,
    log_processor: logs,
    meter_provider,
    ..
  }) = OTEL_GLOBALS.get()
  else {
    return;

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Initialize telemetry exactly once per process — guard the call site behind a OnceLock or AtomicBool
  2. In test harnesses, run telemetry-dependent cases in separate processes
  3. If hit with plain `deno` CLI commands and no custom embedding, report it as a Deno bug in init ordering

Example fix

// before — two components each call init
/* component A */ deno_telemetry::init(opts_a)?;
/* component B */ deno_telemetry::init(opts_b)?; // Err: failed to set otel globals

// after — single guarded entry point
use std::sync::OnceLock;
static TELEMETRY_INIT: OnceLock<()> = OnceLock::new();
fn init_telemetry(opts: Options) -> anyhow::Result<()> {
  if TELEMETRY_INIT.set(()).is_err() {
    return Ok(()); // already initialized in this process
  }
  deno_telemetry::init(opts)
}
Defensive patterns

Strategy: validation

Validate before calling

use std::sync::OnceLock;
static TELEMETRY_INIT: OnceLock<()> = OnceLock::new();

// route every caller through this instead of deno_telemetry::init
fn init_telemetry_once(opts: TelemetryOptions) -> anyhow::Result<()> {
  if TELEMETRY_INIT.set(()).is_err() {
    return Ok(()); // already initialized in this process
  }
  deno_telemetry::init(opts)
}

Prevention

When it happens

Trigger: Embedders or internal code paths invoking deno_telemetry::init more than once per process, e.g. CLI startup init plus an extension or test harness that also initializes telemetry.

Common situations: Custom embedders building on deno_runtime/deno_telemetry; test harnesses that bring telemetry up per test case in one process; refactors that moved init into a shared helper called from two places.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/bd632989637d482b. Report an issue: GitHub.