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
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-pluginfrom 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-checktransitive dependencyFile: Plugin's
node_modulesafterNpm.add()installationRoot cause:
effect@3.21.4depends onfast-check(peer/optional), but OpenCode'sNpm.add()uses@npmcli/arboristwithignoreScripts: true, which skips optional/peer dependencies.Symptom:
import()of the plugin module fails with:Fix: Add
fast-checkas an explicit dependency inpackage.json, or ensure it's installed:npm install fast-check --prefix ~/.cache/opencode/packages/@langfuse/opencode-observability-plugin@latestBug #2: Tracer created before SDK starts
File:
src/langfuse.ts→createLangfuseClient()Root cause:
trace.getTracer()is called beforesdk.start(). At that point, no global TracerProvider is registered, soProxyTracer._getDelegate()returnsNoopTracer. All subsequentstartSpan()calls produce no-op spans that never reach the processor.Fix: Create the tracer AFTER
sdk.start():Bug #3: Bun runtime —
NodeSDK.start()global provider registration not recognizedRoot cause: Even with Bug #2 fixed,
@opentelemetry/api's globaltrace.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
NodeTracerProviderdirectly withprovider.getTracer():Verification
After all three fixes, traces appear correctly in Langfuse:
Related