All articles

Backend

Langfuse + OpenTelemetry Traces Not Showing? Here's the Exact Fix

Traces not appearing in the Langfuse dashboard after setting up OpenTelemetry? Here's exactly what's wrong with the SDK setup and how to fix it — with working config included.

Published
Reading time
15 min read
Written by
Rakesh · DEVMANUSH RAKY
Langfuse + OpenTelemetry Traces Not Showing? Here's the Exact Fix

I spent days debugging why traces were being generated locally (visible in console logs) but not appearing in the Langfuse dashboard. The issue wasn't obvious—no errors in logs, flush operations completing successfully, but traces simply never reached Langfuse. This guide documents the root causes, solutions, and key learnings from fixing a Langfuse OpenTelemetry integration in a production medical simulation platform.

The Problem: Traces Generated But Not Visible

The symptoms were frustrating: traces were visible in console output with correct structure, flush operations were completing successfully, and there were no errors in server logs. However, traces weren't appearing in the Langfuse dashboard, and trace fetch scripts were returning 404 errors. This meant we couldn't monitor LLM operations, debug issues, or track performance metrics—critical functionality for a medical simulation platform.

Root Cause #1: Incorrect OpenTelemetry SDK Setup

The first major issue was using outdated OpenTelemetry SDK v1.x packages with incompatible configuration. We were using @opentelemetry/sdk-trace-base@^1.27.0 and @opentelemetry/sdk-trace-node@^1.27.0, but @langfuse/otel requires OpenTelemetry SDK v2.x packages.

This version mismatch caused multiple problems: API incompatibilities between v1.x and v2.x, changes to NodeTracerProvider API in v2.x, different span processor registration methods, and internal span structure expectations that changed between versions.

// Before (v1.x - incompatible)
{
  "@opentelemetry/sdk-trace-base": "^1.27.0",
  "@opentelemetry/sdk-trace-node": "^1.27.0",
  "@opentelemetry/exporter-trace-otlp-http": "^0.211.0"
}

// After (v2.x - compatible)
{
  "@opentelemetry/sdk-trace-base": "^2.2.0",
  "@opentelemetry/sdk-trace-node": "^2.2.0",
  "@opentelemetry/core": "^2.2.0",
  "@opentelemetry/sdk-node": "^0.208.0",
  "@opentelemetry/semantic-conventions": "^1.38.0"
}

Root Cause #2: Wrong Integration Approach

Instead of using the official LangfuseSpanProcessor, we attempted to manually configure OTLPTraceExporter with a custom wrapper. This approach seemed logical at first—we wanted to add logging and custom headers for authentication. However, this caused critical issues.

The custom wrapper corrupted the span structure before OTLP transformation. The OTLP transformer expected a specific span format, but our wrapper modified spans in ways that broke the transformation process. This resulted in a TypeError: 'Cannot read properties of undefined (reading 'name')' during span conversion. The worst part? These export failures occurred silently without clear errors in application logs.

// Before: Manual OTLP exporter with custom wrapper (BROKEN)
const exporter = new OTLPTraceExporter({
  url: traceUrl,
  headers: { "Authorization": `Basic ${authString}` },
});
const batchProcessor = new BatchSpanProcessor(exporter, {...});
provider.addSpanProcessor(batchProcessor);

// After: Official LangfuseSpanProcessor (WORKING)
const langfuseProcessor = new LangfuseSpanProcessor({
  publicKey: langfusePublicKey,
  secretKey: langfuseSecretKey,
  baseUrl: langfuseBaseUrl,
  environment: process.env.NODE_ENV || 'development',
});
spanProcessors.push(langfuseProcessor);

Root Cause #3: Missing Peer Dependencies

@langfuse/otel requires @opentelemetry/exporter-trace-otlp-http as a peer dependency. We initially removed this package thinking it wasn't needed since we weren't using it directly. This caused module resolution errors: 'Error: Cannot find module @opentelemetry/exporter-trace-otlp-http'.

Even though LangfuseSpanProcessor handles OTLP export internally, it still needs this package as a peer dependency. The application failed to start after removing it.

Root Cause #4: Using NodeTracerProvider Instead of NodeSDK

We were using the outdated NodeTracerProvider initialization approach, which is incompatible with OpenTelemetry v2.x. The NodeTracerProvider.addSpanProcessor() API changed in v2.x, causing 'TypeError: provider.addSpanProcessor is not a function' errors.

NodeSDK is the recommended approach in OpenTelemetry v2.x because it provides automatic context management, better resource handling, proper integration with LangfuseSpanProcessor, and simplified configuration.

// Before: NodeTracerProvider (v1.x approach)
const provider = new NodeTracerProvider({...});
provider.addSpanProcessor(batchProcessor);
provider.register({ contextManager: new AsyncLocalStorageContextManager() });

// After: NodeSDK (v2.x recommended)
const sdk = new NodeSDK({
  resource,
  spanProcessors,
});
sdk.start();

The Solution: Complete Migration to Official Integration

The solution involved switching to the official LangfuseSpanProcessor integration with OpenTelemetry SDK v2.x. Here's what changed:

Final Configuration

Here's the complete working configuration:

import { NodeSDK } from '@opentelemetry/sdk-node';
import { LangfuseSpanProcessor } from '@langfuse/otel';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';

const resource = new Resource({
  [SemanticResourceAttributes.SERVICE_NAME]: 'simxr-backend',
});

const langfuseProcessor = new LangfuseSpanProcessor({
  publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
  secretKey: process.env.LANGFUSE_SECRET_KEY!,
  baseUrl: process.env.LANGFUSE_BASE_URL || 'https://cloud.langfuse.com',
  environment: process.env.NODE_ENV || 'development',
});

const sdk = new NodeSDK({
  resource,
  spanProcessors: [langfuseProcessor],
});

sdk.start();

Required Dependencies

Make sure your package.json includes these dependencies:

{
  "dependencies": {
    "@langfuse/otel": "^4.4.9",
    "@opentelemetry/api": "^1.9.0",
    "@opentelemetry/context-async-hooks": "^1.27.0",
    "@opentelemetry/core": "^2.2.0",
    "@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
    "@opentelemetry/resources": "^1.27.0",
    "@opentelemetry/sdk-node": "^0.208.0",
    "@opentelemetry/sdk-trace-base": "^2.2.0",
    "@opentelemetry/sdk-trace-node": "^2.2.0",
    "@opentelemetry/semantic-conventions": "^1.38.0"
  }
}

Understanding the Flow

Here's how the flow changed:

Before (Broken Flow):

After (Working Flow):

Key Learnings

Use Official Integrations: Always prefer official SDK integrations over manual configuration. Use LangfuseSpanProcessor instead of manually configuring OTLPTraceExporter.

Version Compatibility Matters: Ensure all OpenTelemetry packages are compatible with each other. @langfuse/otel requires OpenTelemetry SDK v2.x, not v1.x.

Don't Wrap Exporters: Custom wrappers can corrupt span structures expected by transformers. Wrapping exporter.export() method caused TypeError in OTLP transformer.

Use NodeSDK in v2.x: NodeSDK is the recommended approach in OpenTelemetry v2.x. NodeTracerProvider API changed, causing 'addSpanProcessor is not a function' error.

Include Peer Dependencies: Even if not used directly, peer dependencies are required. @opentelemetry/exporter-trace-otlp-http needed by @langfuse/otel internally.

Troubleshooting Steps

If you're experiencing similar issues, follow these troubleshooting steps:

Success Indicators

You'll know the integration is working when:

References

For more information, check out:

  • Langfuse
  • OpenTelemetry
  • Observability
  • LLM Monitoring
  • Troubleshooting
  • Node.js
Share

Keep reading