anomalyco / anomalyco/opencode

[FEATURE]: propagate W3C traceparent on outbound LLM HTTP requests

Open
#49,038 4 comments 0 reactions 1 assignee View on GitHub

@kitlangton is already working on this.

Since Sep 14, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Feature hasn't been suggested before.
  • I have verified this feature I'm about to request hasn't been suggested before. Related but different: #46856 asks for W3C trace context in MCP tools/call _meta (per SEP-414); this request is about the outbound HTTP requests to LLM providers (OpenAI, Anthropic, Azure, Copilot, etc.), which is a different surface. #18801 and #28295 were closed for inactivity and covered MCP HTTP/SSE, not LLM inference calls.
Describe the enhancement

OpenCode's outbound LLM HTTP requests do not carry a W3C traceparent header. When OpenCode is used behind a tracing-capable gateway or proxy, the gateway creates server-side spans for each LLM request, but these spans start a new trace because the inbound HTTP request has no trace context. Client-side and server-side spans end up as disjoint traces that can only be correlated by content and timing.

Suggested implementation

OpenCode already has full OTel infrastructure in packages/core/src/observability/otlp.ts: @opentelemetry/api, AsyncLocalStorageContextManager, and the OTLP HTTP trace exporter. The missing piece is injecting the active span context into outbound LLM requests.

Injection point: packages/llm/src/route/executor.ts, in the layer definition (~line 370). Before http.execute(request), inject the traceparent header from the active OTel context:

import { context, propagation } from "@opentelemetry/api"

// In executeOnce, before http.execute:
const carrier: Record<string, string> = {}
propagation.inject(context.active(), carrier)
const traced = Object.entries(carrier).reduce(
  (req, [k, v]) => HttpClientRequest.setHeader(req, k, v),
  request,
)
return yield* http.execute(traced)...

This requires adding @opentelemetry/api as a dependency to the llm package (it's already in the monorepo via packages/core).

Behavior when tracing is not configured: propagation.inject() is a no-op when no propagator is registered (no TracerProvider set up), so no headers are added. Zero behavioral change for users without tracing.

Verified end-to-end

Tested the equivalent fix (W3C traceparent injection via SDK middleware) against a live OTLP-capable gateway. With propagation, the gateway's server span becomes a child of the client's span in a single connected trace:

rag-pipeline (root)
|- retrieve (parent: rag-pipeline)
|- generate (parent: rag-pipeline)
|   \-- chat openai-gpt-5.4 [SERVER] (parent: generate)  <-- gateway span

Without it, the gateway span is in a separate trace.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.