Skip to content

fix(fetch): preserve explicit trace context during propagation - #733

Open
SamP231004 wants to merge 1 commit into
supabase:mainfrom
SamP231004:fix/50006-preserve-trace-context
Open

fix(fetch): preserve explicit trace context during propagation#733
SamP231004 wants to merge 1 commit into
supabase:mainfrom
SamP231004:fix/50006-preserve-trace-context

Conversation

@SamP231004

Copy link
Copy Markdown

When an Edge Function supplies traceparent to fetch() while runtime tracing is enabled, the runtime appends its own context to the same header. The callee receives two comma-separated contexts, which is not a valid W3C traceparent. This change preserves explicitly supplied trace context while retaining automatic trace propagation for requests without it.

Related to supabase/supabase#50006.

Reproduction and cause

The new integration fixture runs a caller and callee as real user workers, with tracing enabled through otelConfig. The caller uses an explicit trace ID different from the incoming request's active trace and invokes the callee through fetch(url, options), fetch(Request), and supabase.functions.invoke() using @supabase/supabase-js@2.112.3.

With the original runtime code, the traced regression fails because the callee receives:

00-b1e669305668dc82c96cc31ce2e6cd99-1a5b74eca03f769f-01, 00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-c24ad962cf8557ac-01

The expected value is the caller's original header:

00-b1e669305668dc82c96cc31ce2e6cd99-1a5b74eca03f769f-01

The SDK preserves the supplied headers. The duplication occurs in the instrumented runtime fetch(), whose propagator setter calls Headers.append().

Change

Before injecting a propagator that owns traceparent, check whether the request already contains that header. If so, skip that propagator. Header lookup is case-insensitive.

Skipping the trace-context propagator preserves traceparent and its associated tracestate together, including an empty or absent tracestate. It also prevents state from a different active trace being attached to an explicit caller context. Other propagators remain eligible, and requests without an explicit traceparent continue to receive runtime-generated context.

Regression coverage

Two native integration tests exercise traced and untraced callers. Each checks all three invocation methods with present, empty, and absent tracestate, plus a request without explicit context. Assertions cover:

  • Exact preservation of the caller's traceparent, including mixed-case input header names.
  • Preservation of present, empty, and absent tracestate.
  • Preservation of explicit baggage and a custom header.
  • Automatic propagation of the incoming trace ID when caller tracing is enabled.
  • Absence of automatically injected context when caller tracing is disabled.

Validation

Verified locally on Ubuntu 22.04 under WSL with the repository's Rust 1.98.0 toolchain and ONNX Runtime 1.20.1:

  • Both new integration tests pass.
  • Removing only the production guard makes the traced test fail with the duplicate header shown above; restoring the guard makes both tests pass again.
  • All four existing outbound rate-limit tests pass, covering traced circular calls and untraced global budgets through both fetch and Node HTTP.
  • Rust formatting, the pinned dprint 0.47.2 checks for the new TypeScript fixtures, and git diff --check pass.
cargo test -p base --test integration_tests test_fetch_preserves_explicit_trace_context --locked -- --nocapture
cargo test -p base --test integration_tests test_outbound_rate_limit --locked -- --nocapture
cargo fmt --all -- --check

These are targeted tests; the full repository test suite was not run.

Hosted verification

The native tests establish the duplicate-injection defect and its fix. They do not establish the reporter's deployed runtime configuration or prove that a hosted gateway replaces the malformed header with a new trace ID. Hosted verification and rollout remain necessary for the linked issue. Automatic inheritance of inbound baggage/tracestate into runtime spans is outside this change.

@mandar1045

mandar1045 commented Sep 5, 2026

Copy link
Copy Markdown

Hey @SamP231004, I took a look at this PR while checking the same issue.

The core change in 26_fetch.js makes sense, but I think an AI assistant has added quite a bit of extra scaffolding and test permutations here . It makes the PR much harder to review and verify for what is really a small engine fix.

I opened a cleaner, minimal version with a single focused integration test in #734 if you'd like to take a look.

@SamP231004

SamP231004 commented Sep 5, 2026

Copy link
Copy Markdown
Author

Thanks for reviewing this @mandar1045 . That’s fair—the broader coverage helped verify the behavior, but I should have trimmed the permutations and SDK dependency before opening the PR.

I looked at #734. The smaller test is easier to review. One detail: the implementation keeps the same propagator guard as #733, while the description says it changes append() to conditional set(). Those approaches differ when preserving the traceparent/tracestate pair.

I’ll simplify #733 to a focused regression test while retaining a small check that automatic propagation still works.

@mandar1045

Copy link
Copy Markdown

ahh!, yes i've updated it

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.

2 participants