@sentry/cloudflare 11: only the first request per isolate reports errors; later captures emit no envelope
維護者通常 1 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 4/5
- 預估耗時
- 3-5 天
- 新手友好度
- 58/100
- Issue 類型
- 缺陷
- 描述清晰度
- 基本清楚
- 活躍度
- 活躍
- 技術堆疊
- typescript
研究方向
Start at the withSentry entry point and inspect wrapRequestHandlerWithInit, especially the waitUntil?.(flushAndDispose(client)) lifecycle described in the report. Reproduce the issue with the provided concurrent burst and beforeEnvelope hook, then verify that reused isolates produce an envelope and transport request for every captured exception without an explicit flush.
由索引模型根據 Issue 內容生成。
描述
Version
@sentry/cloudflare 11.0.0 — regression from 10.75.3, which is unaffected.
Summary
In a Workers fetch handler wrapped with withSentry, only the first request an isolate serves reports errors. Every subsequent request on that same isolate captures an exception that never becomes an envelope — no transport request is made, nothing reaches Sentry, and nothing is logged.
The failure is silent in both directions. On a failing request the SDK still reports itself healthy:
{"initialized":true,"hasClient":true,"enabled":true,"transport":true}
captureException() returns an event id as usual. Only a beforeEnvelope hook reveals that no envelope is ever produced.
Under production traffic, where an isolate serves many requests, this drops the large majority of errors.
Repro
A fetch handler that captures an exception, plus a module-scope counter so each log line says which isolate served the request and how many it had already served:
import * as Sentry from '@sentry/cloudflare';
let isolate: string | undefined;
let served = 0;
const handler = {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
if (!isolate) isolate = crypto.randomUUID().slice(0, 8);
const client = Sentry.getClient();
console.log('diag', JSON.stringify({
isolate, n: ++served,
initialized: Sentry.isInitialized(),
hasClient: Boolean(client),
transport: Boolean(client?.getTransport()),
}));
client?.on('beforeEnvelope', () => console.log('envelope', isolate, served));
client?.on('afterSendEvent', (e, r) => console.log('sent', e.event_id, r?.statusCode));
Sentry.captureException(new Error('probe'));
return new Response('ok', { status: 500 });
},
} satisfies ExportedHandler<Env>;
export default Sentry.withSentry(
(env: Env) => ({ dsn: env.SENTRY_DSN, tracesSampleRate: 0 }),
handler,
);
Deploy, then drive it with concurrent bursts so isolates are reused (sequential curls each got a fresh isolate and all succeeded — reuse is required to see it):
for round in 1 2 3; do
for i in $(seq 1 10); do curl -s -o /dev/null "$URL/probe?i=$round-$i" & done
wait
done
Read the results with wrangler tail --env <env> --format json and group by isolate / n.
Results
Same Worker, same burst, same deploy pipeline — only the SDK version differs.
| SDK | n=1 | n>1 |
|---|---|---|
| 10.75.3 | 8/8 delivered | 5/5 delivered (n=2, n=3) |
| 11.0.0 | 13/13 delivered | 0/16 delivered (n=2…6) |
One isolate under 11.0.0, verbatim:
isolate 809886ef n=1 envelope ✅
n=2 none ❌
n=3 none ❌
n=4 none ❌
n=5 none ❌
n=6 none ❌
29 invocations across 16 isolates on 11.0.0; the correlation with n has no exceptions.
Suspected cause
Not verified, offered as a starting point: wrapRequestHandlerWithInit ends the request with waitUntil?.(flushAndDispose(client)), and the client object demonstrably survives isolate reuse — registering a hook per request causes a later request to log earlier requests' hook callbacks, and the same event_id is reported under two different request labels. A client disposed by request n then appears to be reused by request n+1, where it still passes isInitialized() / getClient() / getTransport() but emits no envelope.
Two things that make this easy to miss
- An explicit
await Sentry.flush()inside the handler masks it entirely — the event is sent during the request, before any dispose. A probe written with a flush shows 100% delivery and hides the bug. - Every health signal stays green.
isInitialized,getClient,getTransport, and the return value ofcaptureExceptionare all indistinguishable between a delivering and a non-delivering request.
Environment
@sentry/cloudflare11.0.0 vs 10.75.3- Cloudflare Workers,
compatibility_date2026-08-15 (sonodejs_compatis on by default) - Hono 4.13.8; the capture happens in Hono's
onError, but the route is an ordinaryfetchhandler underwithSentry tracesSampleRate: 0- Migration followed as documented: the only change for this usage was replacing
sendDefaultPiiwithdataCollection. Nothing in the v10→v11 guide or the 11.0.0 release notes mentions client lifecycle, disposal, or isolate reuse.
- 主要語言
- TypeScript
- 星號
- 8.7k
- 分支
- 1.9k
- 平均合併
- 1 天 15 小時
- 30 天內合併 PR
- 495
環境準備
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
getsentry/sentry-javascript 的其他 Issue
-
Next.js: basePath is concatenated onto absolute router.push hrefs, corrupting navigation transaction names可能已有人在做 @Lms24 於 4 天前認領。 未關閉Browser Bug Next.js Traces
難度 2/5 1-3 小時 新手友好度 75/100
getsentry/sentry-javascript#24672 · 2 則留言 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
javascript
難度 2/5 1-3 小時 新手友好度 75/100
getsentry/sentry-javascript#24200 · 2 則留言 ·
維護者通常 1 天內回覆
-
javascript Task
難度 2/5 1-3 小時 新手友好度 82/100
getsentry/sentry-javascript#24134 · 1 則留言 ·
維護者通常 1 天內回覆
-
Cloudflare Workers javascript Tests
難度 2/5 1-3 小時 新手友好度 78/100
getsentry/sentry-javascript#24051 · 1 則留言 ·
維護者通常 1 天內回覆
-
Bug Bun javascript
難度 2/5 1-3 小時 新手友好度 92/100
getsentry/sentry-javascript#24045 · 1 則留言 ·
維護者通常 1 天內回覆
查看 getsentry/sentry-javascript 的全部 Issue
相似的 Issue
-
難度 2/5 1-3 小時 新手友好度 76/100
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 76/100
rohitg00/agentmemory#1428 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 86/100
boxlite-ai/boxlite#1729 ·
維護者通常 1 天內回覆
-
detectors enhancement good first issue
難度 2/5 1-3 小時 新手友好度 86/100
SM260845/readme-gen#1 ·
-
難度 2/5 1-3 小時 新手友好度 88/100
angular/angularfire#3774 ·
維護者通常 2 天內回覆