Errors

Error hierarchy, error handlers, and debug pages

3 min read 645 words

Error Hierarchy

flowchart TD ChirpError --> ConfigurationError["ConfigurationError\nInvalid config (missing secret_key, etc.)"] ChirpError --> HTTPError["HTTPError\nHTTP-level errors"] HTTPError --> NotFound["NotFound — 404"] HTTPError --> MethodNotAllowed["MethodNotAllowed — 405"]

All Chirp exceptions inherit fromChirpError. HTTP errors carry a status code and detail message.

Raising Errors

Raise HTTP errors in route handlers to trigger error responses:

from chirp import NotFound, HTTPError

@app.route("/users/{id:int}")
async def get_user(id: int):
    user = await db.fetch_one("SELECT * FROM users WHERE id = ?", [id])
    if not user:
        raise NotFound(f"User {id} not found")
    return Template("user.html", user=user)

@app.route("/premium")
def premium():
    if not g.user or not g.user.is_premium:
        raise HTTPError(403, "Premium access required")
    return Template("premium.html")

NotFound

raise NotFound("Page not found")     # 404 with detail message
raise NotFound()                       # 404 with default message

MethodNotAllowed

Raised automatically by the router when a path matches but the HTTP method does not. The response includes anAllowheader listing valid methods and the allowed methods in the body.

ConfigurationError

Raised at startup for invalid configuration:

# These raise ConfigurationError:
# - Using SessionMiddleware without secret_key
# - Using CSRFMiddleware without secret_key
# - Returning Template without kida integration configured

Error Handlers

Register custom error handlers by status code or exception type:

@app.error(404)
def handle_404(request: Request):
    return Template("errors/404.html", path=request.path)

@app.error(500)
def handle_500(request: Request, error: Exception):
    return Template("errors/500.html", error=str(error))

Error handlers support the same return-value system as route handlers. You can return a Template, Fragment, Response, string, or dict.

Handler Signatures

Error handlers support flexible signatures:

# Zero arguments
@app.error(404)
def handle_404():
    return "Not Found"

# Request only
@app.error(404)
def handle_404(request: Request):
    return Template("404.html", path=request.path)

# Request and error
@app.error(500)
def handle_500(request: Request, error: Exception):
    log_error(error)
    return Template("500.html")

Chirp inspects the handler signature and injects the appropriate arguments.

Exception Type Handlers

Handle specific exception types:

from chirp import ValidationError

@app.error(ValidationError)
def handle_validation(request: Request, error: ValidationError):
    return Response(str(error)).with_status(422)

Fragment-Aware Error Handling

When an htmx request triggers an error, Chirp renders error fragments instead of full error pages:

@app.error(404)
def handle_404(request: Request):
    if request.is_fragment:
        return Fragment("errors/404.html", "error_message", path=request.path)
    return Template("errors/404.html", path=request.path)

Built-in error handling automatically returns <div class="chirp-error">snippets for htmx requests.

Terminal Error Formatting

During development, Chirp formats errors for the terminal with structured, readable output instead of raw Python tracebacks.

Template Errors

When a Kida template error occurs, Chirp displays the error with its code, source snippet, and route context:

-- Template Error -----------------------------------------------
K-RUN-001: Undefined variable 'usernme' in base.html:42

     |
> 42 | <h1>{{ usernme }}</h1>
     |

Hint: Did you mean 'username'?
Docs: https://kida.dev/docs/errors/#k-run-001

  Route: GET /dashboard
-----------------------------------------------------------------

Non-Template Errors

For other exceptions, Chirp filters tracebacks to show only application frames (hiding framework and stdlib internals).

Traceback Verbosity

Control terminal traceback style with theCHIRP_TRACEBACKenvironment variable:

Value Behavior
compact App frames only (default)
full Full Python traceback
minimal Single-line error summary
# Show full tracebacks during deep debugging
CHIRP_TRACEBACK=full chirp run

# Minimal output for CI/production
CHIRP_TRACEBACK=minimal chirp run

Streaming and SSE Errors

Errors during chunked streaming or SSE connections use the same formatting pipeline. In debug mode, streaming errors render as a visible<div class="chirp-error"> element in the page, and SSE errors are sent as an event: errormessage the client can handle.

Debug Pages

WhenAppConfig(debug=True), unhandled exceptions render a detailed debug page with:

  • Full traceback with source code context
  • Request details (method, path, headers, query parameters)
  • Application configuration
  • Template error panel with error code, source snippet, and docs link (for Kida errors)

Warning

Debug pages expose internal details. Never enabledebug=Truein production.

Next Steps