Error Hierarchy
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 myapp:app
# Minimal output for CI/production
CHIRP_TRACEBACK=minimal chirp run myapp:app
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.
SSE streams have per-event error boundaries: if a singleFragment fails to render, the error is caught and the stream continues. In debug mode, the failed block is replaced with a <div class="chirp-block-error">element showing the exception inline. In production, the event is silently skipped.
If thecontext_builder() in a reactive stream raises, the entire event is skipped (logged via chirp.reactivelogger) and the stream waits for the next change.
Catastrophic errors (ASGI failures, unrecoverable state) still terminate the stream with anevent: errormessage the client can handle.
Debug Pages
WhenAppConfig(debug=True), unhandled exceptions render a detailed debug page with:
- Full traceback with source code context (framework frames collapsed)
- Request details (method, path, headers, query parameters)
- Application configuration
- Kida template error panel with error code, source snippet, suggestion, and docs link
- Environment section — Python, Chirp, and Kida versions
When a Kida parse error is wrapped (e.g. byTemplateRuntimeError), the debug page prefers the cause's template context (correct file and line) over the wrapper's. Consecutive framework frames (middleware, ASGI adapters) are collapsed into an expandable block to reduce noise.
Warning
Debug pages expose internal details. Never enabledebug=Truein production.
Next Steps
- API Reference -- Complete API surface
- Routes -- Error handlers and route registration
- Configuration -- Debug mode settings