Environment
@sentry/cloudflare 10.69.0
- Cloudflare Workflows
Problem
instrumentWorkflowWithSentry captures context.waitUntil.bind(context) inside each run() call. init() instruments that same context. When Cloudflare reuses a Workflow instance, a later run can therefore capture Sentry’s flush-lock-aware waitUntil wrapper instead of the original function.
At the end of that run, the wrapper schedules flushAndDispose(client) through the captured function. This can register the teardown promise in the same flush lock that client.flush() is waiting to drain, so teardown never settles. In production this surfaced as Worker code had hung immediately after otherwise successful workflow completion.
Application work registered through the instrumented waitUntil should remain tracked; only Sentry’s own teardown must bypass its instrumentation.
Suggested fix
Capture the original function before client initialization, matching the existing RPC wrapper:
const waitUntil = getOriginalWaitUntil(context).bind(context);
const client = init({ ...options, ctx: context, enableDedupe: false });
The CommonJS build can use the corresponding flush.getOriginalWaitUntil(context) export.
Regression shape
A regression test can reuse one instrumented Workflow instance for two runs. During the second run, register controlled pending application work through the instrumented context, return successfully, then release that work and await both the application and teardown promises. With the current implementation the teardown promise remains pending; using getOriginalWaitUntil lets both settle while genuine workflow errors still propagate.
Environment
@sentry/cloudflare10.69.0Problem
instrumentWorkflowWithSentrycapturescontext.waitUntil.bind(context)inside eachrun()call.init()instruments that same context. When Cloudflare reuses a Workflow instance, a later run can therefore capture Sentry’s flush-lock-awarewaitUntilwrapper instead of the original function.At the end of that run, the wrapper schedules
flushAndDispose(client)through the captured function. This can register the teardown promise in the same flush lock thatclient.flush()is waiting to drain, so teardown never settles. In production this surfaced asWorker code had hungimmediately after otherwise successful workflow completion.Application work registered through the instrumented
waitUntilshould remain tracked; only Sentry’s own teardown must bypass its instrumentation.Suggested fix
Capture the original function before client initialization, matching the existing RPC wrapper:
The CommonJS build can use the corresponding
flush.getOriginalWaitUntil(context)export.Regression shape
A regression test can reuse one instrumented Workflow instance for two runs. During the second run, register controlled pending application work through the instrumented context, return successfully, then release that work and await both the application and teardown promises. With the current implementation the teardown promise remains pending; using
getOriginalWaitUntillets both settle while genuine workflow errors still propagate.