8 – How to catch various Guzzle exceptions in s3fs module?

In working on a site with the s3fs module, we have encountered various scenarios that cause page loads to fail with the error, The website encountered an unexpected error. Please try again later. The causes have varied: outbound proxy authentication issues, wrong or missing s3 authentication in the settings.local.php, network failures, etc.. I’m now working on recreating and tracking down some of those issues in the hopes of catching the errors before they cause the page load to fail with an error message in production.

As an example, to simulate an error I truncate the s3fs_file table, clear cache, and remove the settings.local.php file, then try to load the front page, I get the error above. When I look in the error logs, I see:

GuzzleHttpExceptionConnectException: cURL error 35: OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to foo.amazonaws.com:443 (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) in GuzzleHttpHandlerCurlFactory::createRejection() (line 202 of /home/appuser/app/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php).

I tried adding debug() before that line. When I then look in the error logs, it turns out to get called a bunch of times before finally failing. For the call just before the site returns the error, it looks like the request to AWS and subsequently Guzzle originates within getS3Metadata:

#18 /home/appuser/app/web/modules/contrib/s3fs/src/StreamWrapper/S3fsStream.php(1211): AwsAwsClient->__call('headObject', Array)
#19 /home/appuser/app/web/modules/contrib/s3fs/src/StreamWrapper/S3fsStream.php(1071): Drupals3fsStreamWrapperS3fsStream->getS3Metadata('public://styles...')

It seems the earlier calls generate an S3Exception error, which getS3Metadata is set up to catch. The problem is that I’ve tried every variation I can think of or find reference to but I can’t seem to catch GuzzleHttpExceptionConnectException. I’ve tried switching the catch from S3Exception to Throwable which continues to catch the the S3Exception but still doesn’t catch the Guzzle’s ConnectException.

First, what am I missing? Second, am I going about this the right way or should I just resign myself to there being unanticipated failure scenarios and use something like the Error custom pages module to present a slightly more pleasant error page to the user?