Skip to content

fix(cloudflare): Call connect() on the binding for service bindings and DO stubs - #24593

Open
JPeer264 wants to merge 1 commit into
developfrom
jp/cloudflare-connect-binding
Open

JPeer264 wants to merge 1 commit into
developfrom
jp/cloudflare-connect-binding

Conversation

@JPeer264

Copy link
Copy Markdown
Member

Found while fixing #24590: the service binding and Durable Object stub proxies have the same problem with connect(). It is returned unbound, so env.SERVICE.connect() and stub.connect() throw "Illegal invocation" on an instrumented env, with or without RPC trace propagation.

Unlike the Queue fix, these proxies cannot bind every pass-through function: reading .bind on an RPC method returns another RPC property (ping.bind) and would break RPC calls. So only connect() is forwarded to the binding.

Local workerd rejects an incoming CONNECT on a Worker after the this check, so the integration test can only assert that the service binding no longer throws "Illegal invocation". The Durable Object stub call succeeds end to end.

🤖 Generated with Claude Code

@JPeer264
JPeer264 added this pull request to stack #24594 September 22, 2026 14:22
@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 c0d0931. Configure here.

@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 29.23 kB - -
@sentry/browser - with treeshaking flags 27.5 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.4 kB - -
@sentry/browser (incl. Tracing) 51.16 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 51.17 kB - -
@sentry/browser (incl. Tracing, Profiling) 54.16 kB - -
@sentry/browser (incl. Tracing, Replay) 90.75 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.85 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 95.45 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 108.42 kB - -
@sentry/browser (incl. Feedback) 46.76 kB - -
@sentry/browser (incl. sendFeedback) 34.29 kB - -
@sentry/browser (incl. FeedbackAsync) 39.39 kB - -
@sentry/browser (incl. Metrics) 30.25 kB - -
@sentry/browser (incl. Logs) 30.5 kB - -
@sentry/browser (incl. Metrics & Logs) 31.17 kB - -
@sentry/react 30.98 kB - -
@sentry/react (incl. Tracing) 53.44 kB - -
@sentry/vue 36.72 kB - -
@sentry/vue (incl. Tracing) 53.69 kB - -
@sentry/svelte 29.25 kB - -
CDN Bundle 30.93 kB - -
CDN Bundle (incl. Tracing) 51.69 kB - -
CDN Bundle (incl. Logs, Metrics) 33.19 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.66 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.92 kB - -
CDN Bundle (incl. Tracing, Replay) 89.27 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.23 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 95.43 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 97.4 kB - -
CDN Bundle - uncompressed 91.4 kB - -
CDN Bundle (incl. Tracing) - uncompressed 153.76 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.97 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 159.72 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.54 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 273.5 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 279.43 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 287.2 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 293.13 kB - -
@sentry/nextjs (client) 55.78 kB - -
@sentry/sveltekit (client) 51.59 kB - -
@sentry/core/server 39.92 kB - -
@sentry/core/browser 13.63 kB - -
@sentry/node 133.88 kB +0.02% +17 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.4 kB - -
@sentry/node - without tracing 90.39 kB +0.01% +8 B 🔺
@sentry/node - without channel injection 112.42 kB +0.02% +21 B 🔺
@sentry/aws-serverless 98.71 kB +0.03% +22 B 🔺
@sentry/cloudflare (withSentry) - minified 206.51 kB +0.09% +167 B 🔺
@sentry/cloudflare (withSentry) 513.78 kB +0.07% +355 B 🔺

View base workflow run

@JPeer264
JPeer264 marked this pull request as ready for review September 22, 2026 16:53
@JPeer264
JPeer264 requested a review from a team as a code owner September 22, 2026 16:53
@JPeer264
JPeer264 requested review from andreiborza, isaacs and mydea and removed request for a team September 22, 2026 16:53
Comment on lines 65 to 74
return instrumentFetcher((...args) => Reflect.apply(value, target, args));
}

if (prop === 'connect' && typeof value === 'function') {
return (...args: unknown[]) => Reflect.apply(value, target, args);
}

if (
propagateRpcTrace &&
typeof value === 'function' &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The dup() method on instrumented Durable Object stubs is returned unbound. Calling stub.dup() will cause an "Illegal invocation" error.
Severity: HIGH

Suggested Fix

Add a handler for the dup method in the instrumentDurableObjectStub proxy, similar to the existing handler for connect. This will ensure dup is correctly bound using Reflect.apply(value, target, args) before being returned, preventing the "Illegal invocation" error.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
packages/cloudflare/src/instrumentations/instrumentDurableObjectNamespace.ts#L65-L74

Potential issue: The proxy handler for instrumented Durable Object stubs in
`instrumentDurableObjectStub` correctly handles the `connect` method by binding it, but
it fails to do the same for the `dup` method. Both methods are listed in
`STUB_NON_RPC_METHODS` and should be excluded from RPC tracing but still function
correctly. When `dup` is accessed, the proxy falls through and returns the method
unbound. Any subsequent call to `stub.dup()` will fail with an "Illegal invocation"
error because `this` is not correctly bound to the stub instance.

Also affects:

  • packages/cloudflare/src/instrumentations/worker/instrumentEnv.ts:101~110

Did we get this right? 👍 / 👎 to inform future reviews.

Base automatically changed from jp/cloudflare-queue-producer-metrics-binding to develop September 23, 2026 07:21
…nd DO stubs

The service binding and Durable Object stub proxies returned connect()
unbound, so calling it ran the native method with the proxy as `this`
and workerd threw "Illegal invocation". The proxies cannot bind every
function, because reading `.bind` on an RPC method returns another RPC
property, so only connect() is forwarded to the binding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@JPeer264
JPeer264 force-pushed the jp/cloudflare-connect-binding branch from c0d0931 to c0611fe Compare September 23, 2026 07:21

This branch has not been deployed

No deployments
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.

1 participant