Skip to content

fix(observability): propagate W3C traceparent on outbound LLM requests - #49046

Closed
joshreini1 wants to merge 2 commits into
anomalyco:devfrom
joshreini1:feat/traceparent-propagation
Closed

joshreini1 wants to merge 2 commits into
anomalyco:devfrom
joshreini1:feat/traceparent-propagation

Conversation

@joshreini1

@joshreini1 joshreini1 commented Sep 14, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #49038

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Makes OpenCode propagate W3C trace context on outbound LLM requests, so an OTLP-capable gateway or proxy can parent its server span to the client span instead of starting a disconnected trace.

Two changes:

packages/core/src/observability/otlp.ts — register a W3C trace context propagator. tracingLayer() already set a global context manager, but never set a propagator. propagation.inject() falls back to a no-op propagator when none is registered, so it silently wrote nothing. This is why no trace headers were being sent.

packages/opencode/src/provider/provider.ts — inject the trace context in the provider fetch wrapper (~line 1822), which every AI SDK provider request passes through. @opentelemetry/api is already a dependency of this package.

When experimental.openTelemetry is off or OTEL_EXPORTER_OTLP_ENDPOINT is unset, tracingLayer() returns Layer.empty, no propagator is registered, and propagation.inject() writes nothing. The request goes out with identical headers. No behavioral change for users without tracing.

How did you verify your code works?

tsc --noEmit -p packages/opencode/tsconfig.json reports no errors in either changed file.

Ran a real opencode run session (LLM calls plus a bash tool call) against a live OTLP gateway with experimental.openTelemetry: true and OTEL_EXPORTER_OTLP_ENDPOINT set, then queried the gateway trace table. Gateway server spans now share the client trace_id and parent to the client span:

trace_id = 5050cccedec81ab7254e27ab214f28e2   (905 spans)

  ai.streamText.doStream          [INTERNAL]  opencode
    └─ chat openai-gpt-5.4        [SERVER]    gateway    parent=e2d3f0f6795a8bc8
  ai.streamText.doStream          [INTERNAL]  opencode
    └─ chat openai-gpt-5.4        [SERVER]    gateway    parent=8373ea52255379e7
  ai.streamText.doStream          [INTERNAL]  opencode
    └─ chat openai-gpt-5-nano     [SERVER]    gateway    parent=009ecd88908101f8

Each server span's parent_span_id matches the span id from the traceparent header sent on that request. Before the fix, the same session produced client spans in one trace and each gateway server span in its own separate single-span trace.

A second session reproduced this with 6 server spans in a 2413-span trace.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Inject traceparent/tracestate headers from the active OTel span context
into every outbound LLM provider HTTP request. This allows an
OTLP-capable server (AI gateway, observability proxy) to create a child
span linked to the caller's trace, producing a single connected trace.

The injection happens in the request executor layer: before
http.execute(), the global OTel propagator writes trace context headers
into the request. When no span is active (tracing not configured),
propagation.inject() is a no-op and no headers are added.

Closes anomalyco#49038
Relates to anomalyco#46856
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. needs:title labels Sep 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hey! Your PR title [FEATURE]: propagate W3C traceparent on outbound LLM HTTP requests doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@joshreini1 joshreini1 changed the title [FEATURE]: propagate W3C traceparent on outbound LLM HTTP requests feat(llm): propagate W3C traceparent on outbound LLM HTTP requests Sep 14, 2026
@github-actions github-actions Bot removed needs:title needs:compliance This means the issue will auto-close after 2 hours. labels Sep 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

… provider fetch

The previous approach injected traceparent in packages/llm/src/route/executor.ts,
but that layer is not on the path LLM requests take. Two problems, both fixed here:

1. No propagator was registered. propagation.inject() falls back to a no-op
   propagator when none is set, so the carrier came back empty and no headers
   were ever written. tracingLayer() now registers a W3C trace context
   propagator alongside the existing context manager.

2. Wrong injection point. AI SDK providers issue requests through the provider
   fetch wrapper in provider.ts, not the Effect HTTP client in executor.ts.
   Injection moved to the fetch wrapper so it covers every provider.

Reverts the executor.ts and packages/llm/package.json changes, which are no
longer needed.

Verified against a live OTLP gateway: gateway server spans now carry the client
trace_id and parent to the ai.streamText.doStream span.
@joshreini1 joshreini1 changed the title feat(llm): propagate W3C traceparent on outbound LLM HTTP requests fix(observability): propagate W3C traceparent on outbound LLM requests Sep 14, 2026
@joshreini1

Copy link
Copy Markdown
Author

@rekram1-node can you take a look?

@thdxr

thdxr commented Oct 10, 2026

Copy link
Copy Markdown
Member

This branch has been upgraded to V2. We're no longer taking PRs for V1. If you think this is still relevant, please port it over and open it for V2.

— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

@thdxr thdxr closed this Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants