Skip to content

fix(worker): resolve 'Body has already been used' error on retry#5574

Open
colegottdank wants to merge 1 commit intomainfrom
fix/body-already-used-retry-error-v2
Open

fix(worker): resolve 'Body has already been used' error on retry#5574
colegottdank wants to merge 1 commit intomainfrom
fix/body-already-used-retry-error-v2

Conversation

@colegottdank
Copy link
Collaborator

Summary

  • Fixes "Body has already been used" 500 errors that occurred when server-side retries were triggered (for 429 or 5xx responses from providers)
  • Converts ReadableStream body to string before the retry loop so it can be safely reused

Problem

When callProviderWithRetry() retried requests after receiving 429 or 5xx responses from providers, the retry would fail with "Body has already been used" errors. This happened because:

  1. The body in callProps could be a ReadableStream (when RequestBodyBuffer_Remote is used for large requests)
  2. ReadableStream bodies can only be consumed once by fetch()
  3. The first retry attempt consumed the stream, making it unusable for subsequent attempts

Solution

Convert any ReadableStream body to a string at the start of callProviderWithRetry() before entering the retry loop. Strings can be safely reused across multiple fetch() calls.

Test plan

  • TypeScript check passes
  • All 1421 worker tests pass

🤖 Generated with Claude Code

When callProviderWithRetry() retried requests after 429 or 5xx responses,
it would fail with "Body has already been used" errors. This occurred
because ReadableStream bodies can only be consumed once by fetch().

The fix converts any ReadableStream body to a string before entering
the retry loop, allowing the body to be safely reused across retry
attempts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
helicone Skipped Skipped Feb 17, 2026 6:41am
helicone-bifrost Skipped Skipped Feb 17, 2026 6:41am
helicone-eu Skipped Skipped Feb 17, 2026 6:41am

Request Review

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@supabase
Copy link

supabase bot commented Feb 17, 2026

This pull request has been ignored for the connected project bolqqmqbrciybnypvklh because there are no changes detected in supbase/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@supabase
Copy link

supabase bot commented Feb 17, 2026

This pull request has been ignored for the connected project lmahfbbnchpworytrrqk because there are no changes detected in .supabase/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@claude
Copy link
Contributor

claude bot commented Feb 17, 2026

Claude finished @colegottdank's task —— View job


PR Review: Fix 'Body has already been used' Error

Review Checklist:

  • Read and analyze the changed code
  • Check for critical bugs or security issues
  • Evaluate performance implications
  • Review code quality and best practices
  • Provide scored feedback with recommendations

Score: 7/10 - Good fix, minor improvements suggested

Code Analysis

The fix correctly addresses the "Body has already been used" error by converting ReadableStream bodies to strings before the retry loop. Here's my detailed analysis:

✅ What's Good:

  • Correct Problem Identification: The fix properly handles the root cause where ReadableStream can only be consumed once
  • Clean Implementation: The solution is straightforward and follows a logical approach
  • Preserves Functionality: Other body types (string | null) are handled correctly via the pass-through
  • Good Documentation: Clear comments explain why the conversion is necessary
  • Safe Type Handling: Uses instanceof ReadableStream check before processing

⚠️ Areas for Improvement:

1. Error Handling Missing (worker/src/lib/clients/ProviderClient.ts:194-204)

The ReadableStream reading logic lacks error handling. If the stream encounters an error, the function will throw without cleanup:

if (retryableBody instanceof ReadableStream) {
  const reader = retryableBody.getReader();
  try {
    const chunks: Uint8Array[] = [];
    // ... existing code
  } catch (error) {
    reader.releaseLock(); // Clean up the reader
    throw error;
  }
}

2. Performance Consideration (worker/src/lib/clients/ProviderClient.ts:194-204)

For large request bodies, this approach loads the entire content into memory. While necessary for retry functionality, consider:

  • Adding size limits or warnings for very large streams
  • Consider if there are alternative approaches for specific use cases

3. TextDecoder Options (worker/src/lib/clients/ProviderClient.ts:202-203)

The TextDecoder assumes UTF-8 encoding. Consider being explicit:

const decoder = new TextDecoder('utf-8');

4. Memory Optimization (worker/src/lib/clients/ProviderClient.ts:203)

The current approach decodes each chunk separately then joins. More efficient:

// Combine chunks first, then decode once
const combinedChunks = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
  combinedChunks.set(chunk, offset);
  offset += chunk.length;
}
retryableBody = decoder.decode(combinedChunks);

Suggestions Summary:

  • Critical: Add error handling and cleanup for ReadableStream reading
  • Performance: Consider memory implications for large streams
  • Code Quality: Explicit TextDecoder encoding and optimize chunk processing
  • Documentation: Consider adding JSDoc for the function explaining the body conversion behavior

Security Assessment:

No security issues identified. The fix maintains existing security postures and doesn't introduce new attack vectors.

Testing Recommendation:

Consider adding specific tests for:

  • ReadableStream body conversion
  • Error scenarios during stream reading
  • Large stream handling
  • Retry behavior with converted bodies

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant