Skip to content

Commit 51352ee

Browse files
CopilotLms24
andauthored
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 <noreply@anthropic.com> 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>
1 parent a14b306 commit 51352ee

2 files changed

Lines changed: 2 additions & 10 deletions

File tree

packages/cloudflare/src/utils/streaming.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export type StreamingGuess = {
88
* Heuristics:
99
* - No body → not streaming
1010
* - Known streaming Content-Types → streaming (SSE, NDJSON, JSON streaming)
11-
* - text/plain without Content-Length → streaming (some AI APIs)
1211
* - Otherwise → not streaming (conservative default, including HTML/SSR)
1312
*
1413
* We avoid probing the stream to prevent blocking on transform streams (like injectTraceMetaTags)
@@ -20,18 +19,15 @@ export function classifyResponseStreaming(res: Response): StreamingGuess {
2019
}
2120

2221
const contentType = res.headers.get('content-type') ?? '';
23-
const contentLength = res.headers.get('content-length');
2422

2523
// Streaming: Known streaming content types
2624
// - text/event-stream: Server-Sent Events (Vercel AI SDK, real-time APIs)
2725
// - application/x-ndjson, application/ndjson: Newline-delimited JSON
2826
// - application/stream+json: JSON streaming
29-
// - text/plain (without Content-Length): Some AI APIs use this for streaming text
3027
if (
3128
/^text\/event-stream\b/i.test(contentType) ||
3229
/^application\/(x-)?ndjson\b/i.test(contentType) ||
33-
/^application\/stream\+json\b/i.test(contentType) ||
34-
(/^text\/plain\b/i.test(contentType) && !contentLength)
30+
/^application\/stream\+json\b/i.test(contentType)
3531
) {
3632
return { isStreaming: true };
3733
}

packages/deno/src/utils/streaming.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export type StreamingGuess = {
1010
* Heuristics:
1111
* - No body → not streaming
1212
* - Known streaming Content-Types → streaming (SSE, NDJSON, JSON streaming)
13-
* - text/plain without Content-Length → streaming (some AI APIs)
1413
* - Otherwise → not streaming (conservative default, including HTML/SSR)
1514
*
1615
* We avoid probing the stream to prevent blocking on transform streams (like injectTraceMetaTags)
@@ -22,18 +21,15 @@ export function classifyResponseStreaming(res: Response): StreamingGuess {
2221
}
2322

2423
const contentType = res.headers.get('content-type') ?? '';
25-
const contentLength = res.headers.get('content-length');
2624

2725
// Streaming: Known streaming content types
2826
// - text/event-stream: Server-Sent Events (Vercel AI SDK, real-time APIs)
2927
// - application/x-ndjson, application/ndjson: Newline-delimited JSON
3028
// - application/stream+json: JSON streaming
31-
// - text/plain (without Content-Length): Some AI APIs use this for streaming text
3229
if (
3330
/^text\/event-stream\b/i.test(contentType) ||
3431
/^application\/(x-)?ndjson\b/i.test(contentType) ||
35-
/^application\/stream\+json\b/i.test(contentType) ||
36-
(/^text\/plain\b/i.test(contentType) && !contentLength)
32+
/^application\/stream\+json\b/i.test(contentType)
3733
) {
3834
return { isStreaming: true };
3935
}

0 commit comments

Comments
 (0)