Repository navigation
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: [FEATURE]: propagate W3C trace context (traceparent/baggage) in MCP tools/call _meta per SEP-414 #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. [FEATURE]: propagate incoming trace context for HTTP requests #18801 and [FEATURE]: propagate W3C trace context on outbound MCP HTTP/SSE requests #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.
Activity
github-actions commentedon Sep 14, 2026
This issue doesn't fully meet our contributing guidelines.
What needs to be fixed:
[FEATURE]:and the issue body should include the verification checkbox confirming the feature hasn't already been requested.Please edit this issue to address the above within 2 hours, or it will be automatically closed.
This issue might be a duplicate of existing issues. Please check:
If you believe this was flagged incorrectly, please let a maintainer know.
[-]feat: propagate W3C traceparent on outbound LLM HTTP requests[/-][+][FEATURE]: propagate W3C traceparent on outbound LLM HTTP requests[/+]github-actions commentedon Sep 14, 2026
Thanks for updating the issue and following the contributing guidelines! Everything looks good now.
joshreini1 commentedon Sep 14, 2026
End-to-end proof via AGENT_TRACE_TABLE
Ran two requests against a live AI gateway (
openai-gpt-5.4viaDEFAULT_GATEWAY), then queriedTABLE(AGENT_TRACE_TABLE('DEFAULT_GATEWAY')):Test 1 (no traceparent): gateway server span gets its own trace_id, disconnected from any client trace.
Test 2 (with traceparent
00-e2bc2acd...-620bb0e5...-01): gateway server span adopts the client trace context:The server span's
parent_span_id(620bb0e5fee04c14) matches the client span_id from thetraceparentheader. Both spans share the sametrace_id. This is a single connected distributed trace.Without the traceparent header, the gateway's server spans start independent traces with no link back to the client. The PR fix (
propagation.inject()inexecutor.ts) adds this header when OTel tracing is configured.joshreini1 commentedon Sep 14, 2026
Correction on the root cause
My earlier comments pointed at
packages/llm/src/route/executor.tsas the injection point. That was wrong, and testing a build with the change in place showed traces were still disconnected. Two things are actually going on:1. No propagator is registered.
tracingLayer()inpackages/core/src/observability/otlp.tssets a global context manager but never sets a propagator.propagation.inject()falls back to a no-op propagator when none is registered, so it writes nothing to the carrier. Confirmed by logging the carrier during a live run:carrier={}on every request. This is the primary reason no trace headers are sent, and it would defeat an injection call placed anywhere.2.
executor.tsis not on the request path. AI SDK providers issue their HTTP requests through the providerfetchwrapper inpackages/opencode/src/provider/provider.ts(~line 1822), not the Effect HTTP client inpackages/llm/src/route/executor.ts. Injection has to go in the fetch wrapper to cover providers.With a propagator registered and the injection in the fetch wrapper, the same live session produces:
and the gateway server span lands in the client trace:
PR #49046 has been updated with this approach: 2 files, 30 lines.
opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up! You're asking for OpenCode to add a W3C
traceparentheader to its outbound HTTP requests to LLM providers when tracing is configured. Then spans from a tracing gateway or proxy would join OpenCode's client trace instead of starting a new one.The file paths in the issue are out of date on the current
devbranch. The request executor is nowpackages/ai/src/route/executor.ts, and the OTLP setup ispackages/util/src/observability/otlp.ts. Effect'sHttpClient, which that executor uses, already addstraceparentfrom the current span by default. So the gap is probably in providers that go through the AI SDK's ownfetchpath. If you can say which provider/model you tested with and which OpenCode version (opencode --version), that would help locate where the header is missing.Related issues:
tools/call_meta(a different surface)The maintainers will consider this as a feature request.
siglp commentedon Oct 30, 2026
I found a remaining propagation issue on current dev. When request middleware supplies an explicit traceparent, Effect’s HTTP client overwrites it during send and also adds a B3 header from a different trace.
I have a small fix that disables automatic tracer propagation only for requests already containing traceparent. Requests without an explicit parent retain native propagation. A wire-level regression test covers direct headers, middleware replacement, middleware injection, native propagation, and disabled propagation.
Verification: 42 executor tests pass and the AI package typecheck passes.
Tested E2E on agentgateway 1.6.0 a and langfuse 4.71.0