render function in Handler.php not working Laravel 8

This code doesn't work for me (in Laravel 8.74.0):

$this->renderable(function (ModelNotFoundException$e, $request) {
    return response()->json(...);
});

Don't know why, but ModelNotFoundException is directly forwarded to NotFoundHttpException (which is a part of Symfony Component) that used by Laravel and will ultimately triggers a 404 HTTP response. My workaround is checking the getPrevious() method of the exception:

$this->renderable(function (NotFoundHttpException $e, $request) {
  if ($request->is('api/*')) {
    if ($e->getPrevious() instanceof ModelNotFoundException) {
        return response()->json([
            'status' => 204,
            'message' => 'Data not found'
        ], 200);
    }
    return response()->json([
        'status' => 404,
        'message' => 'Target not found'
    ], 404);
  }
});

And then we will know that this exception come from ModelNotFoundException and return a different response with NotFoundHttpException.

Edit

This is why ModelNotFoundException thrown as NotFoundHttpException


In Laravel 8x, You need to Rendering Exceptions in register() method

use App\Exceptions\CustomException;

/**
 * Register the exception handling callbacks for the application.
 *
 * @return void
 */
public function register()
{
    $this->renderable(function (CustomException $e, $request) {
        return response()->view('errors.custom', [], 500);
    });
}

For ModelNotFoundException you can do it as below.

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

public function register()
{
    $this->renderable(function (NotFoundHttpException $e, $request) {
        return response()->json(...);
    });
}

By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering Closure for exceptions of a given type. You may accomplish this via the renderable method of your exception handler. Laravel will deduce what type of exception the Closure renders by examining the type-hint of the Closure:

More info about the error exception


This one is my Handler file:

use Throwable;

   public function render($request, Throwable $exception)
    {
 if( $request->is('api/*')){
   if ($exception instanceof ModelNotFoundException) {
                $model = strtolower(class_basename($exception->getModel()));
              
 return response()->json([
            'error' => 'Model not found'
        ], 404);
            }
  if ($exception instanceof NotFoundHttpException) {
 return response()->json([
            'error' => 'Resource not found'
        ], 404);
                
            }
}
}

This one is only for all request in API route. If you want to catch all request, so remove the first if.