Skip to content

fix(cloudflare): Capture telemetry from untraced Durable Object RPC calls - #24447

Merged
JPeer264 merged 3 commits into
getsentry:developfrom
msnelling:fix/cloudflare-do-rpc-untraced-telemetry
Sep 17, 2026
Merged

JPeer264 merged 3 commits into
getsentry:developfrom
msnelling:fix/cloudflare-do-rpc-untraced-telemetry

Conversation

@msnelling

@msnelling msnelling commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Durable Object RPC methods only got a Sentry client when the call carried __sentry_rpc_meta__. Every other call ran with no client, so its errors, logs and metrics were dropped. That covers calls from uninstrumented workers, calls made outside the request's async context, and every call through jurisdiction(). jurisdiction() itself bypassed instrumentDurableObjectNamespace, so its stubs never propagated a trace.

External RPC calls without metadata now run capture-only (client, isolation scope and flush, but no span), which is what instrumentWorkerEntrypoint already does. jurisdiction() now returns a namespace that is instrumented the same way as the parent.

Some decisions worth calling out:

  • Telling external calls from internal ones. RPC methods are wrapped on the prototype (Cloudflare Agent RPC proxy breaks native private fields #23040), so a this.helper() call reaches the same wrapper as an external call. workerd dispatches an incoming RPC call outside any async context, so the wrapper treats a call made while an invocation is already in flight (isolation scope is not the default) as internal and runs the original method. Returning a proxy of the instance, as instrumentWorkerEntrypoint does, would bring back the private-field breakage from Cloudflare Agent RPC proxy breaks native private fields #23040. That's why the change only touches Durable Objects.
  • startNewTrace for untraced calls, unlike WorkerEntrypoint. A Durable Object instance is long-lived and serves overlapping calls. Without a new trace, every untraced call inherits the propagation context of the default scope, so unrelated calls share one trace id. The overlapping-calls unit test fails without it.

Fixes #24443

  • If you've added code that should be tested, please add tests.
  • Ensure your code lints and the test suite passes (yarn lint) & (yarn test).
  • Link an issue if there is one related to your pull request. If no issue is linked, one will be auto-generated and linked.

Written with AI assistance (Claude Code).

…alls and instrument jurisdiction()

Durable Object RPC methods only ran inside a Sentry client when the call
carried trace metadata, so errors, logs and metrics from any other call
were dropped. External calls without metadata now run capture-only, in a
new trace. Calls the instance makes to its own methods stay unwrapped.

`jurisdiction()` now returns an instrumented namespace, so its stubs
propagate RPC trace context like those from `get()` and `getByName()`.

Fixes getsentry#24442
Fixes getsentry#24443

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@msnelling
msnelling marked this pull request as ready for review September 16, 2026 17:37
@msnelling
msnelling requested a review from a team as a code owner September 16, 2026 17:37
@msnelling
msnelling requested review from isaacs and mydea and removed request for a team September 16, 2026 17:37
@JPeer264 JPeer264 changed the title fix(cloudflare): Capture telemetry from untraced Durable Object RPC calls and instrument jurisdiction() fix(cloudflare): Capture telemetry from untraced Durable Object RPC calls Sep 17, 2026
@JPeer264
JPeer264 requested review from JPeer264 and removed request for isaacs and mydea September 17, 2026 06:55
JPeer264 pushed a commit that referenced this pull request Sep 17, 2026
…4450)

`instrumentDurableObjectNamespace` special-cases `get` and `getByName`,
and binds every other function to the raw target. `jurisdiction()`
therefore returned an uninstrumented namespace, so stubs from a
jurisdiction-restricted namespace got no fetch span and never appended
`__sentry_rpc_meta__`. Apps using data-location jurisdictions (for
example EU residency) lost trace continuity at every Durable Object
call.

`jurisdiction()` now returns a namespace instrumented the same way as
its parent, with the same `propagateRpcTrace` setting.

This has unit coverage only: local workerd throws "Jurisdiction
restrictions are not implemented in workerd", so an integration suite
can't exercise it.

Split out of #24447, which now covers #24443 only.

Fixes #24442

- [x] If you've added code that should be tested, please add tests.
- [x] Ensure your code lints and the test suite passes (`yarn lint`) &
(`yarn test`).
- [x] Link an issue if there is one related to your pull request. If no
issue is linked, one will be auto-generated and linked.

_Written with AI assistance (Claude Code)._

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

@JPeer264 JPeer264 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.

Thanks a lot for your contributions, I highly appreciate it. I added 2 more tests just to check for potential regressions - also to cover that in the future.

@JPeer264
JPeer264 enabled auto-merge (squash) September 17, 2026 12:59
@JPeer264
JPeer264 merged commit 99d40f8 into getsentry:develop Sep 17, 2026
205 checks passed
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.

@sentry/cloudflare: Durable Object RPC methods drop metrics, logs and errors unless the caller propagates a trace

2 participants