How do I return 500 for fatal errors? When I am trying to access Drupal and the database is down, it will return a 500 error. However, when I try to access Drupal when Redis is down, it says:


  Additional uncaught exception thrown while handling exception.  
  
  Original
  Predis\Connection\ConnectionException: Connection refused [tcp://drupal-redis-2:6379] in Predis\Connection\AbstractConnection->onConnectionError() (line 146 of /var/www/corp/sites/all/libraries/predis/src/Connection/AbstractConnection.php).  
  
  Additional
  Predis\Connection\ConnectionException: Connection refused [tcp://drupal-redis-2:6379] in Predis\Connection\AbstractConnection->onConnectionError() (line 146 of /var/www/corp/sites/all/libraries/predis/src/Connection/AbstractConnection.php).


The HTTP code returned, however, is 200. This makes it difficult to detect if a server is down for load balancing purposes.

This question is similar to Site is down due to MySQL error but it returns 200/OK but it remains unanswered and is a little different.
This is a known issue, whose fix didn't make it into the Drupal 7 core so far. The culprit can be found within the _drupal_exception_handler(). If another error occurs while handling an exception, it just returns the messages stated in your question without setting HTTP status codes (possibly to avoid further exceptions).

Compare https://www.drupal.org/project/drupal/issues/2666908

The above issue already has a small RTBC patch that solves the problem by adding a 500 status code to the handling of the second exception:

function _drupal_exception_handler($exception) {
  require_once DRUPAL_ROOT . '/includes/errors.inc';

  try {
    // Log the message to the watchdog and return an error page to the user.
    _drupal_log_error(_drupal_decode_exception($exception), TRUE);
  }
  catch (Exception $exception2) {
    // Add a 500 status code in case an exception was thrown before the 500
    // status could be set (e.g. while loading a maintenance theme from cache).
    drupal_add_http_header('Status', '500 Internal Server Error');

    // Another uncaught exception was thrown while handling the first one.
    // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
    if (error_displayable()) {
      print '&lt;h1&gt;Additional uncaught exception thrown while handling exception.&lt;/h1&gt;';
      print '&lt;h2&gt;Original&lt;/h2&gt;&lt;p&gt;' . _drupal_render_exception_safe($exception) . '&lt;/p&gt;';
      print '&lt;h2&gt;Additional&lt;/h2&gt;&lt;p&gt;' . _drupal_render_exception_safe($exception2) . '&lt;/p&gt;&lt;hr /&gt;';
    }
  }
}


Given the age of that patch, the line numbering differs a little from the current version of bootstrap.inc. But it still applies cleanly and works as expected.

After you patched your Drupal, you should be able to receive the 500 error code at your load balancer, if Redis is down or the error page can't be created for another reason.

Please note, if your Drupal already sent headers/output to the browser before the error handler kicks in, above patch may have unintended side-effects. You may have to test yourself, whether your webserver still generates 500 server errors for these edge cases.