Skip to content

Plugin not sending traces: 3 root causes identified (missing dep, tracer timing, Bun OTEL compat) #7

Closed

Description

@eeeming

Summary

After deep debugging (source-level tracing through both OpenCode's plugin loader and the plugin's own code), I identified 3 bugs that prevent @langfuse/opencode-observability-plugin from sending traces to Langfuse.

All testing done with OpenCode v1.17.13 on macOS (Bun runtime), Langfuse v3.205.1 self-hosted.


Bug #1: Missing fast-check transitive dependency

File: Plugin's node_modules after Npm.add() installation

Root cause: effect@3.21.4 depends on fast-check (peer/optional), but OpenCode's Npm.add() uses @npmcli/arborist with ignoreScripts: true, which skips optional/peer dependencies.

Symptom: import() of the plugin module fails with:

Cannot find package 'fast-check' from '.../effect/dist/esm/FastCheck.js'

Fix: Add fast-check as an explicit dependency in package.json, or ensure it's installed:

npm install fast-check --prefix ~/.cache/opencode/packages/@langfuse/opencode-observability-plugin@latest

Bug #2: Tracer created before SDK starts

File: src/langfuse.ts → createLangfuseClient()

Root cause: trace.getTracer() is called before sdk.start(). At that point, no global TracerProvider is registered, so ProxyTracer._getDelegate() returns NoopTracer. All subsequent startSpan() calls produce no-op spans that never reach the processor.

// ❌ Current code
const traceState = {
    tracer: trace.getTracer(tracerName, PLUGIN_VERSION),  // ← BEFORE sdk.start()
    ...
};
const sdk = new NodeSDK({ spanProcessors: [...] });
yield* Effect.sync(() => sdk.start());  // ← AFTER

Fix: Create the tracer AFTER sdk.start():

const traceState = { tracer: null, ... };
const sdk = new NodeSDK({ spanProcessors: [...] });
yield* Effect.sync(() => sdk.start());
traceState.tracer = trace.getTracer(tracerName, PLUGIN_VERSION);  // ← AFTER sdk.start()

Bug #3: Bun runtime — NodeSDK.start() global provider registration not recognized

Root cause: Even with Bug #2 fixed, @opentelemetry/api's global trace.getTracer()returns aProxyTracerthat delegates to a no-op provider. This appears to be a Bun-specific issue whereNodeSDK.start()callstrace.setGlobalTracerProvider()but the global registry isn't properly shared between@opentelemetry/sdk-nodeand the separately-resolved@opentelemetry/apiin the plugin'snode_modules`.

Fix: Bypass the global API entirely. Use NodeTracerProvider directly with provider.getTracer():

// ❌ Before
import { NodeSDK } from "@opentelemetry/sdk-node";
const sdk = new NodeSDK({ spanProcessors: [...] });
sdk.start();
const tracer = trace.getTracer(name, version);

// ✅ After
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
const provider = new NodeTracerProvider({ spanProcessors: [...] });
provider.register();
const tracer = provider.getTracer(name, version);

Verification

After all three fixes, traces appear correctly in Langfuse:

scope: opencode-langfuse-plugin v0.1.0
sessionId: ses_0c80adcb8ffe97...
environment: development
observations: 3

Related

Activity

  1. bezbac commented on Aug 7, 2026

    @bezbac
    Member

    The plugin is now building most dependencies and only outputting one final index.js which should hopefully fix these issues. Please give the newly released v0.2.0 a try.

    You might need to update your OpenCode config to explicitly reference this version if you’ve already used v0.1.0.

    "plugin": [
      "@langfuse/opencode-observability-plugin@0.2.0",
    ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions