RFR: 8368249: HttpClient: Translate exceptions thrown by sendAsync [v3]

Daniel Fuchs dfuchs at openjdk.org
Thu Oct 23 11:16:03 UTC 2025


On Thu, 23 Oct 2025 10:34:48 GMT, Jaikiran Pai <jpai at openjdk.org> wrote:

>> Then we should wrap - but that could be strange. Especially in those cases where the application does:
>> 
>>     throw new UncheckedIOException(new IOException(msg));
>> 
>> 
>> But yes - maybe you're right. Since send() does rewrap UncheckedIOException into IOException then maybe sendAsync should do so too. Utils.toIOException could be overriden with a method that takes a lambda to convert UncheckedIOException to IOException. Something like:
>> 
>> 
>>      public static IOException toIOException(Throwable cause) {
>> +        return toIOException(cause, UncheckedIOException::getCause);
>> +    }
>> +
>> +    public static IOException toIOException(Throwable cause, Function<UncheckedIOException, IOException> uncheckedIOConverter) {
>>          if (cause == null) return null;
>>          if (cause instanceof CompletionException ce) {
>>              cause = ce.getCause();
>> @@ -522,7 +526,7 @@ public static IOException toIOException(Throwable cause) {
>>          if (cause instanceof IOException io) {
>>              return io;
>>          } else if (cause instanceof UncheckedIOException uio) {
>> -            return uio.getCause();
>> +            return uncheckedIOConverter.apply(uio);
>>          }
>>          return new IOException(cause.getMessage(), cause);
>>      }
>> 
>> 
>> Then sendAsync could call `Utils.toIOException(throwable, IOException::new)`
>
>> I think the application should receive the UncheckedIOException as the top level cause of the ExecutionException and that UncheckedIOException
> 
> Slight correction to that previous comment of mine. In my mind, for a moment I thought UncheckedIOException is a IOException and that's why I said it should be returned as a the instance from `ExecutionException.getCause()`. That's not the case and I think what should really happen is that we treat `UncheckedIOException` just like any other `RuntimeException` and wrap it into a `IOException`. I think we shouldn't be peeking into the `UncheckedIOException.getCause()` at all when constructing that top level `IOException`. That way we will correctly pass along the original exception that was raised by the application code. 
> 
> Very specifically, I think the `Utils.toIOException()` should look like:
> 
> 
> public static IOException toIOException(Throwable cause) {
>     if (cause == null) return null;
>     if (cause instanceof CompletionException ce) {
>         cause = ce.getCause();
>     } else if (cause instanceof ExecutionException ee) {
>         cause = ee.getCause();
>     }
>     if (cause instanceof IOException io) {
>         return io;
>     }
>     return new IOException(cause.getMessage(), cause);
> }

Ok - let's do that. To my surprise there's only one place where toIOException is called. Other places that need an IO simply create a new IO. We could revisit those places and see if they should also call toIOException but that's another story best handled separately.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/27787#discussion_r2454766162


More information about the net-dev mailing list