Search code examples
phplaravellaravel-11exceptionhandler

Why is my Laravel 11 exception handler not working?


Using Laravel 11, exception handling is moved to the bootstrap/app.php file. I'm trying to catch AuthorizationException and pass the user back to the dashboard with the 403 message:

// bootstrap/app.php

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;

return Application::configure(basePath: dirname(__DIR__))
    ...
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (AuthorizationException $e, Request $request) {
            return redirect()
                ->route('dashboard')
                ->withErrors($e->getMessage());
        });
        ...
    })
    ...
    ->create();

But it's not working. This logged in user is gated from seeing the /users endpoint, but it's not being handled by my exception handler. They should be seeing their dashboard. (My .env does show APP_DEBUG=true.)

enter image description here

(Using barryvdh/laravel-debugbar in this screenshot)


Solution

  • Update (thanks @Olivier):

    Laracasts forum answered this wonderfully: https://laracasts.com/discuss/channels/laravel/how-to-catch-a-policy-response-exception


    My Original:

    Changed exception type-hint to just Throwable to inspect whatever gets caught:

    $exceptions->render(function (Throwable $e, Request $request) {
        dd($e);
        ...
    });
    

    ...and it looks like it skipped the AuthorizationException and passed a Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException enter image description here

    Question now is "why?" Why do Laravel docs not give more detail about how to handle common exceptions? Anyways, it's solved, but others might have the same issue, so here's my fix:

    <?php
    
    use Illuminate\Auth\Access\AuthorizationException;
    use Illuminate\Foundation\Application;
    use Illuminate\Foundation\Configuration\Exceptions;
    use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
    
    return Application
        ::configure(basePath: dirname(__DIR__))
        ...
        ->withExceptions(function (Exceptions $exceptions) {
            $exceptions->render(function (Throwable $e) {
                if ( $e instanceof AccessDeniedHttpException ) {
                    /* This may be overly specific, but I want to handle other
                       potential AccessDeniedHttpExceptions when I come
                       across them */
                    if ( $e->getPrevious() instanceof AuthorizationException ) {
                        return redirect()
                            ->route('dashboard')
                            ->withErrors($e->getMessage());
                    }
                }
            });
        })
        ->create();