denoland/deno · error · anyhow::Error

Failed to read env var OTEL_EXPORTER_OTLP_PROTOCOL

Error message

Failed to read env var OTEL_EXPORTER_OTLP_PROTOCOL: {}

What it means

When reading OTEL_EXPORTER_OTLP_PROTOCOL, Deno distinguishes two failure modes: the variable being absent (VarError::NotPresent, treated as default) and every other std::env::VarError — practically only VarError::NotUnicode, i.e. the environment variable holds bytes that are not valid UTF-8. Since Rust env vars are OsString-backed, a non-UTF-8 value aborts telemetry init with this wrapped error.

Solutions

  1. Re-set the variable with a plain ASCII value: OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
  2. Unset it entirely (absent is handled as the default, only invalid bytes are fatal)
  3. Recreate the env_file / secret as UTF-8 text (use stringData rather than data in Kubernetes secrets)

Example fix

# before (env file saved as Latin-1 with BOM-like bytes)
docker run --env-file .env deno --otel app.ts

# after: rewrite file as UTF-8 with a clean value
printf 'OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf\n' > .env
docker run --env-file .env deno --otel app.ts
Defensive patterns

Strategy: validation

Validate before calling

# Fail fast in the launch script if the var holds non-UTF-8 bytes
if [ "${OTEL_EXPORTER_OTLP_PROTOCOL+x}" != "x" ]; then
  unset OTEL_EXPORTER_OTLP_PROTOCOL
elif ! printf '%s' "$OTEL_EXPORTER_OTLP_PROTOCOL" | LC_ALL=C grep -qP '^[\x00-\x7F]*$'; then
  echo "OTEL_EXPORTER_OTLP_PROTOCOL is not valid UTF-8/ASCII; resetting" >&2
  export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
fi

Prevention

When it happens

Trigger: A process whose environment contains OTEL_EXPORTER_OTLP_PROTOCOL encoded in a non-UTF-8 codepage (e.g. Latin-1 bytes from a legacy Windows shell) or binary garbage injected by a misconfigured secrets agent or CI variable-passing mechanism.

Common situations: Windows CMD/PowerShell writing env vars with legacy encodings; Docker env_file saved with a non-UTF-8 editor; Kubernetes secrets created from binary data instead of stringData; shell scripts sourced from files with broken encoding.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/fc615971faaea356. Report an issue: GitHub.

Appendix: source

Thrown at ext/telemetry/lib.rs:1260

  // `protocol` value is only ever the HTTP variant passed to the OTLP HTTP
  // exporter builder.
  let protocol_var = sys.env_var("OTEL_EXPORTER_OTLP_PROTOCOL");
  let (use_console_exporter, use_grpc, protocol) = match protocol_var.as_deref()
  {
    Ok("console") => (true, false, Protocol::HttpBinary),
    Ok("http/protobuf") | Ok("") | Err(std::env::VarError::NotPresent) => {
      (false, false, Protocol::HttpBinary)
    }
    Ok("http/json") => (false, false, Protocol::HttpJson),
    Ok("grpc") => (false, true, Protocol::HttpBinary),
    Ok(protocol) => {
      return Err(deno_core::anyhow::anyhow!(
        "Env var OTEL_EXPORTER_OTLP_PROTOCOL specifies an unsupported protocol: {}",
        protocol
      ));
    }
    Err(err) => {
      return Err(deno_core::anyhow::anyhow!(
        "Failed to read env var OTEL_EXPORTER_OTLP_PROTOCOL: {}",
        err
      ));
    }
  };

  // Define the resource attributes that will be attached to all log records.
  // These attributes are sourced as follows (in order of precedence):
  //   * The `service.name` attribute from the `OTEL_SERVICE_NAME` env var.
  //   * Additional attributes from the `OTEL_RESOURCE_ATTRIBUTES` env var.
  //   * Default attribute values defined here.
  // TODO(piscisaureus): add more default attributes (e.g. script path).
  // The base resource picks up `service.name`, the `telemetry.sdk.*`
  // attributes and any `OTEL_RESOURCE_ATTRIBUTES`/`OTEL_SERVICE_NAME` values.
  let base_resource = Resource::builder().build();
  let sdk_language = base_resource
    .get(&Key::new(TELEMETRY_SDK_LANGUAGE))
    .unwrap();

View on GitHub (pinned to 9ad36f7a2c)