> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lightning-go.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Customize 404 responses, recover from panics, and send structured error responses.

Lightning gives you three complementary tools for error handling: a configurable not-found handler, the `Recovery()` middleware for panics, and `ctx.JSONError()` for returning structured error responses from your handlers.

## Custom 404 Handler

By default, unmatched routes return `404 Not Found` as plain text. Override this by passing a `NotFoundHandler` in your config to `lightning.NewApp()`.

```go theme={null}
func notFound(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusNotFound, lightning.Map{
        "code":    404,
        "message": "the requested resource does not exist",
    })
}

func main() {
    app := lightning.NewApp(&lightning.Config{
        NotFoundHandler: notFound,
    })

    app.Get("/ping", func(ctx *lightning.Context) {
        ctx.JSON(lightning.StatusOK, lightning.Map{"message": "pong"})
    })

    app.Run(":8080")
}
```

The not-found handler runs after the global middleware chain, so any middleware you register with `app.Use()` also applies to 404 responses.

## Recovery Middleware

`Recovery()` catches any panic in a downstream handler or middleware and writes a 500 response instead of crashing the server. It also prints the panic value and stack trace to stderr.

### Default behavior

```go theme={null}
app := lightning.NewApp()
app.Use(lightning.Logger())
app.Use(lightning.Recovery()) // uses the built-in 500 plain-text handler
```

The built-in handler responds with `500 Internal Server Error` as plain text.

### Custom panic handler

Pass your own handler to override the default response:

```go theme={null}
func panicHandler(ctx *lightning.Context) {
    ctx.JSONError(lightning.StatusInternalServerError, "an unexpected error occurred")
}

func main() {
    app := lightning.NewApp()
    app.Use(lightning.Logger())
    app.Use(lightning.Recovery(panicHandler))

    app.Get("/crash", func(ctx *lightning.Context) {
        panic("something went wrong")
    })

    app.Run(":8080")
}
```

<Note>
  `Recovery()` accepts at most one custom handler. The panic value and stack trace are always written to stderr regardless of which handler is active.
</Note>

## Programmatic Error Responses

Use `ctx.JSONError(code, message)` to return a JSON error from any handler. The HTTP status code and the `code` field in the body are set to the same value.

```go theme={null}
app.Get("/admin", func(ctx *lightning.Context) {
    token := ctx.Header("Authorization")
    if token == "" {
        ctx.JSONError(lightning.StatusUnauthorized, "missing authorization header")
        return
    }

    ctx.JSON(lightning.StatusOK, lightning.Map{"message": "welcome"})
})
```

Response body for the unauthorized case:

```json theme={null}
{"code": 401, "message": "missing authorization header"}
```

## Complete Example: Custom 404 and 500 Handlers

```go theme={null}
package main

import (
    "github.com/go-labx/lightning"
)

func notFoundHandler(ctx *lightning.Context) {
    ctx.JSONError(lightning.StatusNotFound, "route not found")
}

func panicHandler(ctx *lightning.Context) {
    ctx.JSONError(lightning.StatusInternalServerError, "internal server error")
}

func main() {
    app := lightning.NewApp(&lightning.Config{
        NotFoundHandler: notFoundHandler,
    })

    app.Use(lightning.Logger())
    app.Use(lightning.Recovery(panicHandler))

    app.Get("/ping", func(ctx *lightning.Context) {
        ctx.JSON(lightning.StatusOK, lightning.Map{"message": "pong"})
    })

    // This route deliberately panics to demonstrate recovery
    app.Get("/panic", func(ctx *lightning.Context) {
        panic("simulated failure")
    })

    app.Run(":8080")
}
```

**Test the handlers:**

```bash theme={null}
# 404 — unregistered route
curl http://localhost:8080/unknown
# {"code":404,"message":"route not found"}

# 500 — recovered panic
curl http://localhost:8080/panic
# {"code":500,"message":"internal server error"}
```
