title: How to handle uncaught exceptions from CompletableFuture.runAsync
body: <p>Our application has some code that runs asynchronously that is failing. Like this:</p>
  
  <pre><code>CompletableFuture.runAsync(
      () -&gt; { throw new RuntimeException("bad"); },
      executorService
  );
  </code></pre>
  
  <p>We want default exception handling code that can catch these errors, in case specific uses forget to handle exceptions (this came from a production bug).</p>
  
  <p>This is apparently tricky. The answer given in <a href="https://stackoverflow.com/questions/2248131/handling-exceptions-from-java-executorservice-tasks">Handling exceptions from Java ExecutorService tasks</a> does not work.</p>
  
  <p>It relies on the task being a <code>Future&lt;?&gt;</code> and then calling <code>get()</code> on it, resulting in the exception being thrown again. But this is not the case for <code>runAsync</code> code.</p>
  
  <p><code>runAsync</code> creates a <code>java.util.concurrent.CompletableFuture.AsyncRun</code> class that seems to try to supress all exceptions. Despite being a <code>Future</code> itself, it does <em>not</em> indicate being <code>isDone()</code>, and seems to provide no way to get exceptions out of it.</p>
  
  <p>So, given the following boilerplate, how should we catch these gnarly exceptions?</p>
  
  <p>Note that we really want something that will catch <em>all</em> unhandled exceptions in <code>runAsync</code> code, not something we can add to each <code>runAsync</code> invocation. It's just too easy to forget to add handling code to each one.</p>
  
  <pre><code>public class ExceptionTest {
      public static void main(String[] args) throws RuntimeException {
          ExecutorService executorService = new ThreadPoolExecutor(
              1, 1, 0L,
              TimeUnit.MILLISECONDS,
              new LinkedBlockingQueue()
          ) {
              protected void afterExecute(Runnable r, Throwable t) {
                  super.afterExecute(r, t);
  
                  // TODO: Magically extract the exception from `r`
              }
          };
  
          CompletableFuture.runAsync(
              () -&gt; { throw new RuntimeException("bad"); },
              executorService
          );
      }
  }
  </code></pre>