=== Error Handler

Jooby catches application exceptions using the javadoc:ErrorHandler[] class. By default, the javadoc:DefaultErrorHandler[] produces a simple HTML page or a JSON response based on the request's `Accept` header, and logs the exception.

.HTML output:
----
Not Found
message: Page not found
status code: 404
----

.JSON output:
----
{
  "message": "Page not found",
  "status": 404,
  "reason": "Not Found"
}
----

.Log output:
----
GET /xx 404 Not Found
io.jooby.exception.StatusCodeException: Not found
  at ...
----

The javadoc:exception.StatusCodeException[] is a generic exception that lets you explicitly specify an HTTP status code:

[source, java]
----
throw new StatusCodeException(StatusCode.FORBIDDEN);
throw new StatusCodeException(StatusCode.NOT_FOUND);
----

Several standard Java exceptions are automatically mapped to default status codes:

* `IllegalArgumentException` (and subclasses): `400 BAD_REQUEST`
* `NoSuchElementException` (and subclasses): `400 BAD_REQUEST`
* `FileNotFoundException` (and subclasses): `404 NOT_FOUND`
* `Exception` (and all other subclasses): `500 SERVER_ERROR`

To map a custom exception to a specific status code, register it using the `errorCode` method:

[source, java]
----
{
  errorCode(MyException.class, StatusCode.UNPROCESSABLE_ENTITY);
}
----

==== Custom Error Handler

You can override the default behavior and provide a custom global error handler using the javadoc:Router[error, io.jooby.ErrorHandler] method:

.Global Error Handler
[source, java, role = "primary"]
----
{
  error((ctx, cause, statusCode) -> {                                      // <1>
    Router router = ctx.getRouter();
    router.getLog().error("Found {} error", statusCode.value(), cause);    // <2>
    
    ctx.setResponseCode(statusCode);
    ctx.send("Encountered a " + statusCode.value() + " error");            // <3>
  });
}
----

.Kotlin
[source, kotlin, role = "secondary"]
----
{
  error { ctx, cause, statusCode ->                                        // <1>
    val router = ctx.router
    router.log.error("Found {} error", statusCode.value(), cause)          // <2>
    
    ctx.responseCode = statusCode
    ctx.send("Encountered a ${statusCode.value()} error")                  // <3>
  }
}
----

<1> Register a global (catch-all) exception handler.
<2> Log the error.
<3> Send a custom error response to the client.

You can use the javadoc:Context[render, java.lang.Object] method inside the error handler to delegate the response to a registered javadoc:MessageEncoder[] or javadoc:TemplateEngine[].

The next example produces an HTML or JSON response based on the client's `Accept` header using content negotiation:

.Error Handling with Content Negotiation
[source, java, role = "primary"]
----
import static io.jooby.MediaType.json;

{
  install(new MyTemplateEngineModule());                      // <1>
  install(new MyJsonModule());                                // <2>

  error((ctx, cause, statusCode) -> {
    ctx.getRouter().getLog().error("Error: {}", statusCode.value(), cause);
    
    Map<String, Object> errorData = Map.of("message", cause.getMessage());

    if (ctx.accept(json)) {                                   // <3>
      ctx.render(errorData);                                  // <4>
    } else {
      ctx.render(new ModelAndView("error.template", errorData)); // <5>
    }
  });
}
----

.Kotlin
[source, kotlin, role = "secondary"]
----
import io.jooby.MediaType.json

{
  install(MyTemplateEngineModule())                           // <1>
  install(MyJsonModule())                                     // <2>

  error { ctx, cause, statusCode ->
    ctx.router.log.error("Error: {}", statusCode.value(), cause)
    
    val errorData = mapOf("message" to cause.message)

    if (ctx.accept(json)) {                                   // <3>
      ctx.render(errorData)                                   // <4>
    } else {
      ctx.render(ModelAndView("error.template", errorData))   // <5>
    }
  }
}
----

<1> Install a <<ecosystem-modules-template-engines, template engine>> module.
<2> Install a <<ecosystem-modules-json, JSON module>>.
<3> Check if the `Accept` header prefers `application/json`.
<4> Render the JSON response if matched.
<5> Fallback to rendering an HTML template.

===== Catch by Code

In addition to the global error handler, you can register handlers for specific HTTP status codes:

.Status Code Handler
[source, java, role = "primary"]
----
import static io.jooby.StatusCode.NOT_FOUND;

{
  error(NOT_FOUND, (ctx, cause, statusCode) -> {
    ctx.send(statusCode);   // <1>
  });
}
----

.Kotlin
[source, kotlin, role = "secondary"]
----
import io.jooby.StatusCode.NOT_FOUND

{
  error(NOT_FOUND) { ctx, cause, statusCode ->
    ctx.send(statusCode)    // <1>
  }
}
----

<1> Send a silent `404` response to the client.

In this example, we silence all `404` responses by bypassing the logging system and sending an empty response body.

[TIP]
====
The javadoc:Context[send, io.jooby.StatusCode] method sends an empty HTTP response with the specified status code.
====

===== Catch by Exception

You can also register handlers for specific exception types. This is useful for intercepting business-logic exceptions before they hit the global handler:

.Exception Type Handler
[source, java, role = "primary"]
----
{
  error(MyBusinessException.class, (ctx, cause, statusCode) -> {
    // Log and handle MyBusinessException specifically
  });
}
----

.Kotlin
[source, kotlin, role = "secondary"]
----
{
  error(MyBusinessException::class) { ctx, cause, statusCode ->
    // Log and handle MyBusinessException specifically
  }
}
----

include::problem-details.adoc[]
