Node.js OpenTelemetry troubleshooting
Most Node.js instrumentation problems come from how and when the SDK is loaded. Work through the Node-specific checks below, then the shared checks at the bottom.
CommonJS vs ESM loading
Section titled “CommonJS vs ESM loading”The auto-instrumentations patch libraries as they are loaded, so the SDK must start before your application imports anything. How you achieve that depends on your module system.
CommonJS (the default; require throughout your code): load the SDK with --require, which runs before your entry file.
node --require ./instrumentation.js server.jsES modules ("type": "module" in package.json, or .mjs files): --require cannot patch modules loaded with import. Start the SDK with --import and register the OpenTelemetry loader hook so imported libraries are patched:
node \ --experimental-loader=@opentelemetry/instrumentation/hook.mjs \ --import ./instrumentation.mjs \ server.jsConvert the initialization module to ESM syntax:
import { NodeSDK } from '@opentelemetry/sdk-node';import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter(), metricReader: new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter(), }), logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())], instrumentations: [getNodeAutoInstrumentations()],});
sdk.start();
process.on('SIGTERM', () => { sdk.shutdown().finally(() => process.exit(0));});Missing spans
Section titled “Missing spans”If the service reports metrics but no traces, or traces are missing spans you expect:
- Load order. The most common cause. The SDK must start before the framework is imported. Confirm
--require/--importpoints at your instrumentation file, or that itsimportis the first line of your entry file. A framework imported before the SDK starts is never patched. - Manual spans not ended. A span is only exported after
span.end()runs. Ensureend()is called in afinallyblock so it runs even when the operation throws. - Unsupported library or version. The auto-instrumentations cover specific libraries and version ranges. A library outside those ranges emits no spans; add manual spans around it.
- Wrong runtime (Next.js). Routes running on the Edge runtime are not instrumented by a
NodeSDK. Confirm the route runs on the Node.js runtime and that you instrument through@vercel/otel. - Sampling. If you set
OTEL_TRACES_SAMPLERto a ratio-based sampler, low-frequency requests may be dropped. Remove the override to confirm the sampler is the cause.
Exporter connectivity
Section titled “Exporter connectivity”If the SDK starts but no data reaches Osuite, the exporter is failing to send. Enable diagnostic logging to see export attempts — add this to the top of the initialization module:
const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);Then check for these causes in the log output:
- Endpoint format.
OTEL_EXPORTER_OTLP_ENDPOINTmust include thehttps://scheme and port443, for examplehttps://ingest.<region>.osuite.io:443. The HTTP/protobuf exporter appends the signal path (/v1/traces,/v1/metrics,/v1/logs) itself — do not add it. - Header format.
OTEL_EXPORTER_OTLP_HEADERSmust bex-osuite-ingest-token=<your-ingest-token>. Multiple headers are comma-separatedkey=valuepairs. - Authentication (
404). The ingest token is missing, mistyped, or rotated. Ingest matches the token at the edge, so an unrecognised token returns404rather than401/403. Issue a fresh token in Settings → API Keys. - DNS or network (
ENOTFOUND,ECONNREFUSED). The host cannot reach the ingest endpoint. Confirm outbound access to port443and that the region hostname is correct. - TLS errors. A corporate proxy or custom certificate authority is intercepting the connection. Configure the proxy or trust store so the host can complete the TLS handshake to the ingest endpoint.
Standard checks
Section titled “Standard checks”No data in Osuite after a few minutes? Work through these checks.
- Endpoint — confirm the exporter targets exactly
ingest.<region>.osuite.io:443for your region, over TLS. - Token — confirm the ingest header carries a valid
<your-ingest-token>and has not been rotated. - Pipeline — confirm the signal you expect (traces, logs, or metrics) is wired into an active pipeline with the
otlp/osuiteexporter attached. - Export errors — check the application or Collector logs for OTLP export failures (auth, DNS, TLS, connection refused).
- Network — confirm the host has outbound access to the ingest endpoint on port
443. - Timing — allow up to a minute for the first data to appear before assuming a failure.
Still stuck? Ask the Investigation Agent or contact support.