fix(worker): resolve 'Body has already been used' error on retry#5574
fix(worker): resolve 'Body has already been used' error on retry#5574colegottdank wants to merge 1 commit intomainfrom
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 3 Skipped Deployments
|
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
Claude finished @colegottdank's task —— View job PR Review: Fix 'Body has already been used' ErrorReview Checklist:
Score: 7/10 - Good fix, minor improvements suggestedCode AnalysisThe fix correctly addresses the "Body has already been used" error by converting ✅ What's Good:
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:
3. TextDecoder Options (worker/src/lib/clients/ProviderClient.ts:202-203)The 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:
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:
|
Summary
ReadableStreambody to string before the retry loop so it can be safely reusedProblem
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:bodyincallPropscould be aReadableStream(whenRequestBodyBuffer_Remoteis used for large requests)ReadableStreambodies can only be consumed once byfetch()Solution
Convert any
ReadableStreambody to a string at the start ofcallProviderWithRetry()before entering the retry loop. Strings can be safely reused across multiplefetch()calls.Test plan
🤖 Generated with Claude Code