Repository navigation
fix(api): keep response body when client sync fails - #998
Conversation
ClientSyncingMiddleware reads the response body to sync the piggybacked client. When that sync threw (for example, a client that fails to decode), the catch blocks returned the original response, whose body was already consumed. Retrofit then read an empty body and the call failed with ClerkResult.Failure(UNKNOWN), even though the endpoint's own payload was valid. The middleware now rebuilds the response from the buffered body on every path, so a client sync failure is only logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: clerk/clerk-android/.coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Limit details: You’ve used all 4 included reviews currently available. Your 60 included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour. 📝 WalkthroughWalkthroughThe middleware now rebuilds and returns the saved response body after caught parsing or processing errors. A new test checks this behavior for a hosted-auth response with a malformed piggybacked client. ChangesClient response recovery
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The change preserves readable endpoint responses when client synchronization fails. No actionable merge-blocking risk is established by the supplied evidence. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
swolfand
left a comment
There was a problem hiding this comment.
The body is now restored on the caught failure paths, and the test proves it. Non-blocking: an exception outside the three caught types (for example one thrown from Clerk.updateClient) still loses the body, same as before. A finally would close that gap. LGTM.
Summary
ClientSyncingMiddleware.syncResponsereads the response body withbody.string(), which uses up the OkHttp body, so it can sync the piggybackedclient. On success it rebuilt the response from that string. But if syncing threwSerializationException,IOExceptionorIllegalArgumentException, the catch blocks only logged and returned the original response, whose body had already been read.So any client that failed to decode broke a valid call. This happens when the piggybacked
clienthas a missing required field or a new enum value with no fallback. Retrofit read an empty body and the call came back asClerkResult.Failure(UNKNOWN), even though the endpoint's ownresponsepayload was fine.The response is now rebuilt from the saved body string after the try/catch, so it happens on every path. A failed client sync is logged and the endpoint's body is passed through unchanged.
Testing
intercept preserves response body when piggybacked client fails to decodeinClientSyncingMiddlewareTestserves a 200 with a validresponseand aclientwhose session is missing required fields. It asserts the body after the middleware matches the original exactly and thatClerk.clientis unchanged. Before this change it failed withexpected:<[{ "response": ... }]> but was:<[]>, and it passes after../gradlew :source:api:testDebugUnitTest :source:api:spotlessCheck :source:api:detektpasses on JDK 21 (762 tests, 0 failures).Overlap with open PRs
syncResponselines ~67–77 inside the try, plussyncClientFromResponsebelow. This PR only touches lines ~80–91 (it moves the rebuild and return to after the catch blocks) and leaves those lines unchanged.ClientSyncingMiddlewareTest.kt, at the imports, around line 128, and appended after line 422. This PR adds its test at line ~341, away from all three spots.Not in this PR
When the client decode fails, any
SignInCompleted/SignUpCompletedevents decoded from the same response are still dropped. That's becauseauthEvents.forEach(Clerk.auth::send)runs inside the same try, after the client sync.🤖 Generated with Claude Code
Summary by CodeRabbit