Laravel, PHP

Exception in the exceptions handler


I’ve stumbled upon this issue couple of months ago, when our Laravel based backend server stopped logging and handling exception that was thrown in a specific flow that included a file upload.

The easy part was to find this exception, a quick peek in Apache’s logs revealed what was hiding: FileNotFoundException. But the file existed, the upload was successful and i had no idea which file Willis was talking ’bout. After a while i found the real exception, some edge case data integrity issue. The small problem was fixed quickly, but the bigger problem just began; there is a case where our exception handler fails for unknown (yet) reason.

To make a long story short – there was a problem (or intentional design) in httpFoundation\File implementation that didn’t played nicely with our attempt to capture the request URL which caused the exception and log it. So eventually –

Request::capture()->url();

In our exception handler caused another exception. And there you go, Exception in the exceptions handler.

Handling exceptions in the exception handler

As you may know, Laravel’s exception handler extends ExceptionHandler and have 2 methods:

  • report – that is responsible for reporting the exception, there you may log it, send it to external bug management tools such as New Relic or anything else.
  • render – that is responsible for creating the HTTP response that will be sent back to the browser.

After report method finished processing whatever you defined as a proper exception handling, it will pass the exception to it’s parent (ExceptionHandler) report method, unless of course you had a problem in the handler.

So how would you handle an exception in the handler itself?

A simple try-catch-finally block was the solution for this exception-seption.
Consider the following modification of Laravels report method:

public function report(Exception $e) {
	
	try {
	
		// Try to execute exception handler
		Log::error("Exception ... ");
		
		// ... Report to New Relic
		// ... Blame the engineers
	
	} catch(Exception $handlerException) {
	
		// Report also the handler's Exception
		parent::report($handlerException);
	
	} finally {
	
		// Whether the try part succeeded or not, report the original exception
		return parent::report($e);
	}
}

The logic is simple. try to execute your handler, catch if something goes wrong and report it, and then finally report the original exception that caused the exception that activated the exception handler. In this case, if god forbid, your handler will break, nothing will go unnoticed, not the original exception, and not the exception in the handler.