Skip to content

feat(cloudflare)!: Replace enableRpcTracePropagation with rpcTracePropagationBindings - #23486

Open
JPeer264 wants to merge 7 commits into
developfrom
jp/rpc-trace-propagation-targets
Open

feat(cloudflare)!: Replace enableRpcTracePropagation with rpcTracePropagationBindings#23486
JPeer264 wants to merge 7 commits into
developfrom
jp/rpc-trace-propagation-targets

Conversation

@JPeer264

@JPeer264 JPeer264 commented Aug 21, 2026

Copy link
Copy Markdown
Member

closes #23233

This PR existed because of this comment: #23363 (comment)

We already have an option tracePropagationTargets and they are set to a safe default list in the browser (because in the browser we can't enable it for all addresses by default). To keep it consistent rpcTracePropagationTargets is the equivalent for RPC calls, while this has the same concerns as the browser - we can't target all RPC calls OOTB, as this would lead to issues as seen in #23233

We now remove the enableRpcTracePropagation: boolean entirely with a allow list rpcTracePropagationTargets option, that is empty by default and is therefore the equivalent of enableRpcTracePropagation: false (in a later PR we have a prefilled allowlist, like in the browser, when users do have our Vite plugin enabled).

rpcTracePropagationTargets only has to be enabled on the sender worker. The receiving worker does not have to do anything, as we automatically detect if there is a payload from us included. If there is, we strip it - it should be safe enough to listen to this by default, as we already have the last RPC parameter properly prefixed.

(when this is being backported to v10, we keep enableRpcTracePropagation: boolean, deprecate it and overrule enableRpcTracePropagation when rpcTracePropagationTargets is set (so the allow list is stronger than enabling it for everything - the receiving worker won't change for the backport)

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.57 kB - -
@sentry/browser - with treeshaking flags 26.92 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 26.82 kB - -
@sentry/browser (incl. Tracing) 48.58 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 48.6 kB - -
@sentry/browser (incl. Tracing, Profiling) 51.51 kB - -
@sentry/browser (incl. Tracing, Replay) 88.04 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.44 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 92.75 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 105.44 kB - -
@sentry/browser (incl. Feedback) 45.81 kB - -
@sentry/browser (incl. sendFeedback) 33.36 kB - -
@sentry/browser (incl. FeedbackAsync) 38.46 kB - -
@sentry/browser (incl. Metrics) 29.51 kB - -
@sentry/browser (incl. Logs) 29.8 kB - -
@sentry/browser (incl. Metrics & Logs) 30.43 kB - -
@sentry/react 30.31 kB - -
@sentry/react (incl. Tracing) 50.79 kB - -
@sentry/vue 35.69 kB - -
@sentry/vue (incl. Tracing) 50.82 kB - -
@sentry/svelte 28.59 kB - -
CDN Bundle 30.36 kB - -
CDN Bundle (incl. Tracing) 49.07 kB - -
CDN Bundle (incl. Logs, Metrics) 32.56 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 50.95 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 72.98 kB - -
CDN Bundle (incl. Tracing, Replay) 86.56 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.44 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 92.33 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.25 kB - -
CDN Bundle - uncompressed 89.97 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.69 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 96.26 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.38 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.36 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 266.12 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 271.79 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279.81 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.48 kB - -
@sentry/nextjs (client) 53.33 kB - -
@sentry/sveltekit (client) 49.03 kB - -
@sentry/core/server 65.08 kB - -
@sentry/core/browser 52.38 kB - -
@sentry/node 121.51 kB +0.02% +21 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 85.19 kB - -
@sentry/node - without tracing 87.63 kB +0.03% +22 B 🔺
@sentry/aws-serverless 95.77 kB +0.03% +26 B 🔺
@sentry/cloudflare (withSentry) - minified 199.52 kB +0.02% +26 B 🔺
@sentry/cloudflare (withSentry) 495.82 kB +0.08% +368 B 🔺

View base workflow run

@JPeer264
JPeer264 marked this pull request as ready for review August 23, 2026 06:28
@JPeer264
JPeer264 requested a review from a team as a code owner August 23, 2026 06:28
@JPeer264
JPeer264 requested review from andreiborza and isaacs and removed request for a team August 23, 2026 06:28
@JPeer264

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit d68e2df. Configure here.

@JPeer264
JPeer264 force-pushed the jp/rpc-trace-propagation-targets branch from d68e2df to 74e4152 Compare August 25, 2026 11:55
Comment thread packages/cloudflare/src/instrumentations/worker/instrumentEnv.ts

@isaacs isaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mostly looks good, and does solve the intended problem. I think there's an improvement that ought to be done to make the gating match what the comment says, and a possible improvement to match core's trace propagation RegExp handling, but once those are addressed (or dismissed for good reason) then lgtm.

Comment on lines +201 to +202
* Propagation over `stub.fetch()` and service binding `fetch()` uses HTTP headers and is not
* affected by this option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be misreading this, but it says here that it's not gating on this option, but then in instrumentEnv in packages/cloudflare/src/instrumentations/worker/instrumentEnv.ts on line 97, it does this:

      if (!shouldPropagateRpcTrace(String(prop))) {
        return item;
      }

which is where it uses this option, and that's before the instrumentDurableObjectNamespace(item) and instrumentFetcher calls. So I think it is affected by this option, then, and maybe shouldn't be?

Suggestion: always apply instrumentDurableObjectNamespace and the JSRPC fetch wrap, and move shouldPropagateRpcTrace(...) down so it guards only the appendRpcMeta decision, to around instrumentEnv.ts line 117 and the equivalent call in instrumentDurableObjectNamespace.ts on line 55.

That'll need the binding name threaded into instrumentDurableObjectNamespace, but that's a pretty minor change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is an insane catch tbh! It's a good idea to always apply it on fetch calls, as this wouldn't break anything, even on uninstrumented workers. The actual RPC calls are the issue - I'll update that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// Strings must match a binding name exactly, without this, an entry of `DB` would also enable
// propagation for a binding named `MY_DB`. Regular expressions still give pattern matching.
return (bindingName: string) => stringMatchesSomePattern(bindingName, targets, true);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably get the same defensive fix as core did in ee5a772, so that /g and /y flags don't mess things up. Either use matchesTracePropagationTargets (which is for this purpose) or call normalizeRegExpTarget like it does.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, also, this should be case-insensitive if the goal is to match tracePropagationTargets, since it became case insensitive in that commit as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be case-insensitive if the goal is to match tracePropagationTargets, since it became case insensitive in that commit as well.

Yes I totally. I planned this to be in a separate PR so this PR can be backported to v10 with the same functionality and the case-insensitive change lives only in v11. Will prepare that today.

so that /g and /y flags don't mess things up

that is a good point, I would have missed that. Same as above I'll keep that in v11 to match things properly

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow-up PR: #23625

}

if (!options?.enableRpcTracePropagation) {
if (!shouldPropagateRpcTrace(String(prop))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the comment on client.ts, this should move down so that it only guards what the JSDoc comment says gates on it.

Suggestion: always apply instrumentDurableObjectNamespace and the JSRPC fetch wrap, and move shouldPropagateRpcTrace(...) down so it guards only the appendRpcMeta decision, to around instrumentEnv.ts line 117 and the equivalent call in instrumentDurableObjectNamespace.ts on line 55.

That'll need the binding name threaded into instrumentDurableObjectNamespace, but that's a pretty minor change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covered with #23486 (comment)

return isObjectLike(item) || typeof item === 'function';
}

const instrumentedBindings = new WeakMap<object, unknown>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is shared across all envs, so it can leak to everything in the same isolate. Could be a problem, since this method is called from a bunch of different places, each with its own options object.

Recommend: key the cache on (binding, decision) or move it inside the per-instrumentEnv closure.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify that I think I can't follow on this. What is an "env" in this case? In practice each DurableObject, Workflow and WorkerEntrypoint to have separate isolates (at least with my tests on prod) and they do not share any memory - so in practice the options wouldn't change. If I read it correctly this comment would only be true if two DurableObjects with different options.rpcTracePropagationBindings occur on one isolate, right?

Comment thread MIGRATION.md Outdated
);
```

- The `instrumentPrototypeMethods` option of `instrumentDurableObjectWithSentry` was removed. A Durable Object instruments its RPC methods unconditionally now, so there is nothing to replace it with on the receiver.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend adding a note here that a Durable Object's prototype methods are now replaced unconditionally, rather than just "there is nothing to replace it with on the receiver", because that sounds a bit like "nothing happens".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being used as just a presence test, so it could use hasRpcMeta no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is now gone, so we don't check for that anymore. I'm using the "check" from inside wrapMethodWithSentry now

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combined with ac98cbb

return captureMethod;
}

const tracedMethod = wrapMethodWithSentry(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low/question: Previously this method was only generated lazily, if the RPC trace prop was enabled, but now it's being built every time. Is it possible/worthwhile to generate both lazily, since only one of them ends up being used?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I just realized that tracedMethod is kinda the same as boundMethod above, so it could be combined into one

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread packages/cloudflare/src/client.ts Outdated
* ```
*/
enableRpcTracePropagation?: boolean;
rpcTracePropagationTargets?: Array<string | RegExp>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use the TracePropagationTargets type from core, clearer communication of intent.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, also, this is kind of confusingly named, since it's not matched against URLs, but just against binding env strings? Consider: rpcTracePropagationBindings

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use the TracePropagationTargets type from core, clearer communication of intent.

Makes sense. I'll use that one

this is kind of confusingly named, since it's not matched against URLs, but just against binding env strings? Consider: rpcTracePropagationBindings

That is an amazing name. Love it!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JPeer264 JPeer264 changed the title feat(cloudflare)!: Replace enableRpcTracePropagation with rpcTracePropagationTargets feat(cloudflare)!: Replace enableRpcTracePropagation with rpcTracePropagationBindings Aug 26, 2026
Comment thread packages/cloudflare/src/instrumentations/worker/instrumentEnv.ts
@JPeer264
JPeer264 force-pushed the jp/rpc-trace-propagation-targets branch from 8a2c709 to 7e69829 Compare August 26, 2026 07:41
@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

JS-3338

JPeer264 and others added 7 commits August 26, 2026 12:14
…pagationTargets

`enableRpcTracePropagation: true` propagated trace context to every Durable Object
namespace and service binding on `env`. RPC carries that context as a trailing
argument, and only a Sentry-instrumented receiver strips it again, so any receiver
the user does not own saw an extra argument.

`rpcTracePropagationTargets` names the bindings to propagate to instead, mirroring
`tracePropagationTargets`. Strings match a binding name exactly, regular expressions
match by pattern.

The option is now caller-only. Receivers no longer take a switch: a Durable Object
instruments its RPC methods unconditionally, matching what a WorkerEntrypoint already
did. `extractRpcMeta` only strips a trailing argument that actually carries
`__sentry_rpc_meta__`, so a call arriving without metadata is untouched.

BREAKING CHANGE: `enableRpcTracePropagation` is removed. Replace it on the caller with
`rpcTracePropagationTargets` listing the bindings you call, and drop it from receivers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@JPeer264
JPeer264 force-pushed the jp/rpc-trace-propagation-targets branch from 862d405 to f6bdbcb Compare August 26, 2026 09:16
@JPeer264
JPeer264 requested a review from isaacs August 26, 2026 14:44
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.

Cloudflare RPC trace propagation changes method arguments for uninstrumented receivers

2 participants