Skip to content

flushAndDispose in 10.41.0 crashes when WorkerEntrypoint RPC methods use Sentry tracing #19589

@msnelling

Description

@msnelling

Environment

  • @sentry/cloudflare version: 10.41.0 (regression from 10.40.0)
  • Cloudflare Workers runtime with WorkerEntrypoint RPC (service bindings)
  • Uses withSentry() wrapper for the default export, and WorkerEntrypoint classes exported separately

Description

After upgrading from @sentry/cloudflare 10.40.0 to 10.41.0, we get TypeError: Cannot read properties of undefined (reading 'add') when RPC methods on WorkerEntrypoint classes use Sentry tracing (e.g., startSpan, continueTrace).

Root Cause

10.41.0 introduced flushAndDispose() which replaces flush(2000) in all request completion paths (request.js, handler.js, wrapMethodWithSentry.js). The new ServerRuntimeClient.dispose() sets this._promiseBuffer = undefined. When a WorkerEntrypoint RPC method later tries to use Sentry tracing, it encounters the disposed client and crashes on _promiseBuffer.add().

The mechanism

  1. Worker handles a fetch request via withSentry() → Sentry client is created
  2. Request completes → flushAndDispose(client) runs → dispose() sets _promiseBuffer = undefined
  3. An RPC call arrives at a WorkerEntrypoint method (e.g., SessionsRpc.validateSession)
  4. WorkerEntrypoint methods are not wrapped by withSentry() — they are separate exports
  5. The RPC handler uses Sentry tracing (startSpan, continueTrace) which creates a span
  6. When the span ends, the SDK calls captureEvent()_process()_promiseBuffer.add()
  7. Crash: _promiseBuffer is undefined because the client was disposed

Why this worked in 10.40.0

In 10.40.0, flush(2000) was called at request completion — the client remained fully functional with all internal state intact. There was no dispose() method, no flushAndDispose() helper, and _promiseBuffer was never set to undefined.

Reproduction

// Worker entry (auth.ts)
import { withSentry } from './sentry-config';
import { SessionsRpc } from './sessions.rpc';

const app = new Hono();
// ... routes ...

// Default export wrapped with Sentry
export default withSentry({ fetch: app.fetch.bind(app) }, 'auth-worker');

// WorkerEntrypoint exported separately — NOT wrapped by withSentry
export { SessionsRpc };
// sessions.rpc.ts
import { WorkerEntrypoint } from 'cloudflare:workers';
import * as Sentry from '@sentry/cloudflare';

export class SessionsRpc extends WorkerEntrypoint {
  async validateSession(ctx, request) {
    // This crashes with "Cannot read properties of undefined (reading 'add')"
    // because the Sentry client was disposed after the last fetch request
    return Sentry.startSpan({ name: 'validateSession' }, async () => {
      // ... session validation logic ...
    });
  }
}

Workaround

Pin @sentry/cloudflare to 10.40.0.

Suggested Fix

The flushAndDispose pattern is fundamentally incompatible with the Cloudflare Workers model where:

  1. The same isolate handles both fetch requests and WorkerEntrypoint RPC calls
  2. RPC calls don't go through withSentry/wrapRequestHandler
  3. A disposed client may still be referenced by subsequent operations

Possible solutions:

  • Don't dispose() the client in Workers — use flush() only (revert to 10.40.0 behavior)
  • Add a guard in _process() to check if _promiseBuffer exists before calling .add()
  • Mark disposed clients so that getClient() returns undefined for them, preventing use

Diff evidence

# @sentry/core server-runtime-client.js (10.40.0 → 10.41.0)
+ dispose() {
+   for (const hookName of Object.keys(this._hooks)) {
+     this._hooks[hookName]?.clear();
+   }
+   this._hooks = {};
+   this._eventProcessors.length = 0;
+   this._integrations = {};
+   this._outcomes = {};
+   (this)._transport = undefined;
+   (this)._promiseBuffer = undefined;  // <-- THIS CAUSES THE CRASH
+ }

# @sentry/cloudflare request.js (10.40.0 → 10.41.0)
- waitUntil?.(flush(2000));
+ waitUntil?.(flushAndDispose(client));

Metadata

Metadata

Assignees

Labels

Projects

Status

Waiting for: Product Owner

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions