JCC Express

The Basics

Error handling

Introduction

JCC Express MVC registers HTTP error handling after routes are built. Two pieces matter in order:

  1. 404 handler — for requests that never matched a route.
  2. Error middleware — receives failures passed to next(error) or thrown from async controller code.

Route actions are wrapped so rejected promises and synchronous throws from controllers are forwarded to the error middleware automatically.


404 — no matching route

When no route matches the request, the framework responds with its built-in 404 page.


Application error middleware

Errors are handled in a fixed order:

AuthorizationException

Thrown when a FormRequest subclass’s authorize() returns true (meaning “not allowed” in this framework’s convention). Response:

  • If req.expectsJson() and the request is not Inertia: 403 JSON { message }.
  • Otherwise: flash key error with message "Forbidden", status 403, redirectBack() (see Response).

Validation failures

Covers validation errors from req.validate() / validate().

  • JSON (expects JSON, not Inertia): 422 with { errors: ... } — field names mapped to messages.
  • Web: 422 redirectBack() with validation flashes for templates.

Details of validation and flashes are in Validation, Request, and Session.

Blade / view engine errors

View rendering failures return 500 JSON { errors: message }.

Everything else (generic server errors)

  • APP_DEBUG=true: detailed HTML error page, or 500 JSON with stack and error when the client expects JSON.
  • Otherwise: static 500 error page.

Propagating errors from your code

Rejections and synchronous throws from controller actions are already wired through the router, so in most cases a simple throw or an await on a failing promise is enough.

If you prefer explicit control, use try / catch and forward with next(error):

TypeScript
class UserController {
  async show({ next }: HttpContext) {
    try {
      const user = await User.find(id);
      return user;
    } catch (error) {
      next(error);
    }
  }
}

Calling next(error) skips the remaining middleware stack and jumps to the error middleware.

Middleware — The same pattern applies: call next(err) with an Error (or subclass) to forward the failure.

Manual JSON errors — For full control over the response, you can return res.status(n).json(...) directly inside a route and never call next.


Helper types and exceptions

AppErrorError subclass with optional type and cause; useful when you want a stable err.type for custom handling.

Other exported errors (for example SocialiteAuthError, MissMatchTokenException) follow normal Error handling unless you map them in custom middleware.


Process-level rejections

Unhandled promise rejections are logged; they do not automatically send an HTTP response. Prefer handling failures inside the request pipeline so clients get a proper status and body.


Summary

  • 404 — unmatched routes → framework not-found page.
  • 403AuthorizationException → JSON or flash + redirectBack.
  • 422 — validation errors → JSON errors or redirectBack with flashes.
  • 500 — other errors → debug detail when APP_DEBUG=true, otherwise generic error page.
  • Throw or next(error) from controllers and middleware to use the pipeline.