From a14b30605bdce24991db2ccacf88a41c42a34404 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:04:42 +0000 Subject: [PATCH 1/2] Initial plan From 51352ee7b433fe7fc06e51028fdc77757e93ab83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:32:45 +0000 Subject: [PATCH 2/2] fix(cloudflare,deno): Remove overly aggressive text/plain streaming classification The `classifyResponseStreaming` function classified `text/plain` responses without a `Content-Length` header as streaming. This heuristic was too aggressive because `new Response('some text')` creates a `text/plain` response without Content-Length, causing simple non-streaming responses to take the streaming code path. In the streaming path, the span end and transaction flush are delayed until a stream monitor completes, which can cause timing issues when the response is passed between Durable Objects and Workers in the Cloudflare runtime, leading to flaky test timeouts. Removing this rule ensures simple text/plain responses are processed through the non-streaming (immediate span end + flush) code path. Known streaming content types (SSE, NDJSON, JSON streaming) are still correctly detected. Fixes #20209 Co-Authored-By: Claude Agent-Logs-Url: https://github.com/getsentry/sentry-javascript/sessions/b58267a8-c6d6-4fc0-b4fb-82b5843bbf70 Co-authored-by: Lms24 <8420481+Lms24@users.noreply.github.com> --- packages/cloudflare/src/utils/streaming.ts | 6 +----- packages/deno/src/utils/streaming.ts | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/cloudflare/src/utils/streaming.ts b/packages/cloudflare/src/utils/streaming.ts index fee67cbb9f2a..b479edee06a7 100644 --- a/packages/cloudflare/src/utils/streaming.ts +++ b/packages/cloudflare/src/utils/streaming.ts @@ -8,7 +8,6 @@ export type StreamingGuess = { * Heuristics: * - No body → not streaming * - Known streaming Content-Types → streaming (SSE, NDJSON, JSON streaming) - * - text/plain without Content-Length → streaming (some AI APIs) * - Otherwise → not streaming (conservative default, including HTML/SSR) * * We avoid probing the stream to prevent blocking on transform streams (like injectTraceMetaTags) @@ -20,18 +19,15 @@ export function classifyResponseStreaming(res: Response): StreamingGuess { } const contentType = res.headers.get('content-type') ?? ''; - const contentLength = res.headers.get('content-length'); // Streaming: Known streaming content types // - text/event-stream: Server-Sent Events (Vercel AI SDK, real-time APIs) // - application/x-ndjson, application/ndjson: Newline-delimited JSON // - application/stream+json: JSON streaming - // - text/plain (without Content-Length): Some AI APIs use this for streaming text if ( /^text\/event-stream\b/i.test(contentType) || /^application\/(x-)?ndjson\b/i.test(contentType) || - /^application\/stream\+json\b/i.test(contentType) || - (/^text\/plain\b/i.test(contentType) && !contentLength) + /^application\/stream\+json\b/i.test(contentType) ) { return { isStreaming: true }; } diff --git a/packages/deno/src/utils/streaming.ts b/packages/deno/src/utils/streaming.ts index 045a104c5e93..be9a6e4051ed 100644 --- a/packages/deno/src/utils/streaming.ts +++ b/packages/deno/src/utils/streaming.ts @@ -10,7 +10,6 @@ export type StreamingGuess = { * Heuristics: * - No body → not streaming * - Known streaming Content-Types → streaming (SSE, NDJSON, JSON streaming) - * - text/plain without Content-Length → streaming (some AI APIs) * - Otherwise → not streaming (conservative default, including HTML/SSR) * * We avoid probing the stream to prevent blocking on transform streams (like injectTraceMetaTags) @@ -22,18 +21,15 @@ export function classifyResponseStreaming(res: Response): StreamingGuess { } const contentType = res.headers.get('content-type') ?? ''; - const contentLength = res.headers.get('content-length'); // Streaming: Known streaming content types // - text/event-stream: Server-Sent Events (Vercel AI SDK, real-time APIs) // - application/x-ndjson, application/ndjson: Newline-delimited JSON // - application/stream+json: JSON streaming - // - text/plain (without Content-Length): Some AI APIs use this for streaming text if ( /^text\/event-stream\b/i.test(contentType) || /^application\/(x-)?ndjson\b/i.test(contentType) || - /^application\/stream\+json\b/i.test(contentType) || - (/^text\/plain\b/i.test(contentType) && !contentLength) + /^application\/stream\+json\b/i.test(contentType) ) { return { isStreaming: true }; }