Fix NPE in NettyResponseChannel when root cause exception has null message#3263
Fix NPE in NettyResponseChannel when root cause exception has null message#3263crliao wants to merge 1 commit into
Conversation
…ssage CompletableFuture.orTimeout() creates a TimeoutException with no message. Utils.getRootCause() returns that exception, and calling getMessage() on it returns null, causing an NPE at getErrorResponse() line 593. Add null check before using the root cause message to set FAILURE_REASON_HEADER. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3263 +/- ##
=============================================
- Coverage 64.24% 51.24% -13.00%
+ Complexity 10398 8687 -1711
=============================================
Files 840 931 +91
Lines 71755 79542 +7787
Branches 8611 9526 +915
=============================================
- Hits 46099 40764 -5335
- Misses 23004 35400 +12396
- Partials 2652 3378 +726 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| String rootMessage = Utils.getRootCause(cause).getMessage(); | ||
| if (rootMessage != null) { | ||
| errReason = new String( | ||
| rootMessage.replaceAll("[\n\t\r]", " ").getBytes(StandardCharsets.US_ASCII), |
There was a problem hiding this comment.
will Utils.getRootCause(cause) return null
There was a problem hiding this comment.
No — getRootCause(cause) cannot return null here. If the input is non-null, the loop starts with throwable = t (non-null) and only advances while throwable.getCause() != null; when the chain ends it returns the current throwable, still non-null. It only returns null when the input is null. At this call site, cause has already passed the instanceof RestServiceException check on the enclosing if, so it is guaranteed non-null.
There was a problem hiding this comment.
at line 586 if (cause instanceof RestServiceException)
Problem & Solution Overview
CompletableFuture.orTimeout()constructs aTimeoutExceptionwith no message (new TimeoutException()). When this exception is the root cause of aRestServiceException,Utils.getRootCause(cause).getMessage()returnsnull, causing an NPE inNettyResponseChannel.getErrorResponse()at line 593.This fires when
shouldSendFailureReasonreturns true — i.e. when the request hasSEND_FAILURE_REASON=true(set byNamedBlobPutHandler,S3MultipartUploadPartHandler, andUndeleteHandler). Any named blob PUT whose ID conversion times out will hit this path, logging a spurious NPE stack trace on top of the 503.Fix: null-check the root cause message before using it. If null,
errReasonstaysnulland noFAILURE_REASON_HEADERis set — same behavior as whenshouldSendFailureReasonreturns false.Testing Done
setFailureReasonNullMessageNoNpeTestinNettyResponseChannelTest: wraps a barenew TimeoutException()(no message) inside aRestServiceExceptionwithSEND_FAILURE_REASON=true, sends through embedded channel, asserts 503 response with no NPE and noFAILURE_REASON_HEADER.setFailureReasonInResponseTeststill passes.Notes for Reviewers
Root cause of null message:
CompletableFuture.orTimeout()JDK source callsnew TimeoutException()with no string argument. The NPE only fires on theshouldSendFailureReason=truecode path.Author Checklist