Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

oscaribarra25's avatar

Catch 404 Error in API

Hi everyone,

I am a bit lost in how to capture a 404 error in an API route.

I have this in the routes\api.php file

Route::middleware('auth:sanctum')->put('/v1/activations/change_status/{activation}', [ActivationController::class, 'change_status']);

which is handled by the following controller action

public function change_status(ChangeActivationStatus $request, Activation $activation)
{
	... the rest of the code goes here
}

The thing that when I send a valid activation id as the parameter say for example

[endpoint_base_url]/api/v1/activations/change_status/42

It works as expected but if I send an invalid id say for example

[endpoint_base_url]/api/v1/activations/change_status/43

Then it returns a 404 Not Found error to Postman. My guess is that this is happening because when the controller is trying to find an Activation model for the id = 43 it is coming null and throwing the Not Found exception which is ok but I will like to catch this and generate a customized JSON response error for example.

{
    "success": false,
    "message": "There is no activation with the provided id."
}

But I am lost on how to catch the event and generate this customized JSON response.

0 likes
5 replies
jlrdw's avatar

Once a token is issued and in use there shouldn't be an issue. Is the user already logged in and a token issued? Or is this a case where a user used wrong credentials or doesn't have credentials yet?

Either way, this should be done during validation.

Look at https://www.youtube.com/watch?v=Ql5z9TjXWLY and notice the validation part prior to the token being issued.

In your code I have no idea what change_status is. Use your developer tools in the browser to help debug.

The underscore may be causing problems.

See https://laracasts.com/discuss/channels/laravel/changing-laravel-form-validation-status-to-200-from-422?page=1&replyId=917505

oscaribarra25's avatar

Hi @jlrdw I think I didn't explained my self very well. This is not actually related to authentications or tokens. Authentication is working fine as well as authorization all of this using tokens.

Regarding change_status this is just a url part as I stated in the route

/v1/activations/change_status/{activation}

Activation is just a Model that is related to DB table activations which has an activation_status_id field that has the current activation status (which by the way is what this API resource is intended for the change the activation current status to a new status).

When I send a valid activation_id the action is working as expected. The controller action loads the correct activation from the database in the $activation parameter. Then the action is performed and corresponding response is returned.

The thing is that when I send an invalid activation_id (one that doesn't exist in the database) then API call is throwing error 404 which is correct but is it sending that before reaching the action controller body so I am not able to catch it and compose a JSON response. As a result an HTML response is being sent back to Postman.

How can I catch this error and send a correct JSON response?

oscaribarra25's avatar

This looks like the correct way to do it but I tried it and I am still getting an HTML response instead of a JSON response.

See my app/Exceptions/Handler.php class below

<?php

namespace App\Exceptions;

use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * The list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    public function render($request, Throwable $exception)
    {
        if ($exception instanceof ModelNotFoundException && $request->wantsJson())
        {
            return response()->json([
                'success' => false,
                'message' => 'Not found!'
            ], 404);
        }

        return parent::render($request, $exception);
    }
}

I tried using

public function render($request, Exception $exception)

as stated in that solution but Laravel was telling me that the render function was not compatible and when I saw the parent render it was Throwable rather than Exception so I changed it to Throwable.

oscaribarra25's avatar

Thanks @jlrdw with your response I got in the correct path but maybe that solution was for a prior Laravel version so I started looking for an alternative solution and I found this https://stackoverflow.com/questions/56401115/laravel-api-returns-a-view-404-error-instead-of-json-error

Which guided me to this https://laravel.com/docs/10.x/errors#rendering-exceptions

So the correct way to do it was (for Laravel 10 at least)

<?php

namespace App\Exceptions;

use Illuminate\Http\Request;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * The list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });

        $this->renderable(function (NotFoundHttpException $e, Request $request) {
            if ($request->is('api/*')) {
                return response()->json([
                    'success' => false,
                    'message' => 'Record not found.'
                ], 404);
            }
        });
    }
}

Again thanks a lot

Please or to participate in this conversation.