Skip to content

PLA2-36 | Fix OAuth refresh never triggering on 401 with no body#2309

Merged
cmgrote merged 1 commit intomainfrom
PLA2-36
Mar 11, 2026
Merged

PLA2-36 | Fix OAuth refresh never triggering on 401 with no body#2309
cmgrote merged 1 commit intomainfrom
PLA2-36

Conversation

@cmgrote
Copy link
Collaborator

@cmgrote cmgrote commented Mar 11, 2026

Summary

  • Root cause: In HttpURLConnectionClient.requestStream(), when getErrorStream() returns null for a non-2xx response (e.g. a 401 with no body), the fallback called getInputStream() — which throws IOException for 4xx/5xx status codes. That exception was caught and wrapped as ApiConnectionException, hiding the status code from HttpClient.shouldRetry() so refreshToken() was never called.
  • Fix: For error status codes where getErrorStream() is null, return an AtlanResponseStream with an empty ByteArrayInputStream. This preserves the HTTP status code so the 401 reaches shouldRetry() and triggers the OAuth token refresh as intended.
  • Test: Added a WireMock-backed regression test (HttpURLConnectionClientTest) that stubs a 401 with no body and asserts the response code is 401 (not an exception).

Customer impact

Guidewire reproduced this while purging ~12k assets; 401s occur at the ~10-minute token expiry boundary (Zendesk #119550).

Test plan

  • New regression test testOAuth401WithNoBodyReturnsResponseCode passes
  • Full SDK unit test suite passes (394 tests, 0 failures)
  • spotlessApply formatting applied

🤖 Generated with Claude Code

When getErrorStream() returns null for a 401 response (no body), the
previous fallback called getInputStream() which throws IOException for
non-2xx status codes. That IOException was caught and wrapped as an
ApiConnectionException, hiding the 401 from HttpClient.shouldRetry(),
so refreshToken() was never called and the OAuth token was never
refreshed — causing the client to exhaust all retries until restart.

Fix: for error status codes (non-2xx) where getErrorStream() returns
null, return an AtlanResponseStream with an empty ByteArrayInputStream
instead of calling getInputStream(). This preserves the HTTP status
code so the 401 is visible to shouldRetry() and triggers the OAuth
refresh as intended.

Reproducer: Guidewire purging ~12k assets hit the ~10-min token expiry
boundary (Zendesk #119550).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Chris (He/Him) <cgrote@gmail.com>
Copy link

@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.

@cmgrote cmgrote merged commit 6fad21f into main Mar 11, 2026
7 checks passed
@cmgrote cmgrote added the bug Something isn't working label Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant