Error Handling

  1. AppError
  2. Default error pages
  3. Panics don’t take the server down
  4. APP_DEBUG
  5. xr dev’s build-status banner
  6. Next

AppError

Every handler in this framework returns Result<impl IntoResponse, AppError> (see Controllers & Requests for the full enum). ? propagates naturally from any sqlx/AppError- returning call, the same as any other Rust Result-returning function:

pub async fn show(post: Post) -> Result<impl IntoResponse, AppError> {
    let author = post.user().await?;   // AppError::Internal on a DB failure
    larust_support::auth::authorize(post.can_manage(&user).await?)?; // 403 if false
    Ok(view!("posts.show", { post }))
}

Default error pages

A generated app registers its error pages once, at boot:

app.with_error_pages(larust_core::ErrorPages {
    not_found: larust_support::error_view!("404"),
    internal: larust_support::error_view!("500"),
})

error_view!("404") looks for resources/views/errors/404.blade.xr in your app and compiles it the same way view! compiles any other template; drop a file there to override either page. With no file present, it compiles to Larust’s own built-in default page instead - a plain, real 404/500 you never have to build from scratch just to have something reasonable in production.

Panics don’t take the server down

Every request runs behind tower_http’s CatchPanicLayer - if a handler panics (an unwrapped None, an index out of bounds, whatever), that one request gets a 500 response and the rest of the server keeps running completely unaffected. This is standard tower-http behavior, not something Larust built itself, but it’s on unconditionally in every generated app.

APP_DEBUG

# .env
APP_DEBUG=true

With APP_DEBUG=true, an AppError::Internal/Config (or a caught panic) renders a real, descriptive HTML page instead of a generic message: the error’s own message, its full source() chain, and (for a panic) the panic message itself. This is the single most useful thing to have on while actually building a feature - a broken query tells you why it broke, right there in the browser, instead of a bare “internal server error.”

Never enable APP_DEBUG in production. It’s exactly as dangerous as Laravel’s own APP_DEBUG=true in production: full error detail - potentially including query text, file paths, and internal state - goes straight to whoever’s request triggered it. Every .env.example this framework generates ships it true for local dev with this warning attached; double-check it’s false (the default when the var is unset) before a real deploy.

With APP_DEBUG=false (or unset), the same failures render the plain default (or your own custom) 404/500 page instead - no detail leaked, just logged server-side via tracing::error!.

xr dev’s build-status banner

Worth knowing about separately from request-time errors: while running xr dev, a small fixed banner appears in any open browser tab the moment a rebuild starts, and again if that build fails - because otherwise a request landing on the old, still-running process mid-rebuild gets a confident, correct-looking response for code that’s already been changed underneath it, which is a confusing thing to debug if you don’t know it’s happening. A failed build never takes the site down; the last known-good version keeps serving until a new build succeeds. See Your First App.

Next

You’ve now seen the whole request lifecycle - routing, controllers, validation, templates, sessions, and errors. Database covers models, migrations, and relationships next.