Start With The Contract
When a page or fragment swaps incorrectly, run the contract checker before guessing from screenshots:
chirp check app:app
For CI or release branches:
chirp check app:app --warnings-as-errors
chirp checkcatches the failures that most often turn into blank or wrong DOM:
missing template blocks, route-name collisions, OOB registrations that no
layout satisfies, route-directory metadata mismatches, reactive block typos,
form-contract gaps, local Kida component-call mistakes, trusted-markup review
points, sensitive-looking template output, and known htmx footguns.
Then Enable Debug Mode
Use the development CLI when possible:
chirp dev app:app
Or configure the app directly:
from chirp import App, AppConfig
app = App(AppConfig(debug=True))
Debug mode enables richer errors, template reloads, debug headers, fragment validator warnings, and the browser DevTools overlay.
Use Browser DevTools
Open the app and pressCtrl+Shift+Dto open Chirp DevTools.
For browser-capable agents, export records from the page:
window.ChirpHtmxDebug.help()
window.ChirpHtmxDebug.exportRecordsJson()
The exported records include htmx requests, errors, SSE connections and events, View Transition events, render plans, and Swap Doctor diagnostics. Use this when the visual symptom is vague but the request/target/render intent should be precise.
Symptom Table
| Symptom | Likely cause | First check |
|---|---|---|
A whole page appears inside a<div> |
Handler returnedTemplate(...)for an htmx request |
UsePage(...) or Fragment(...); debug fragment validator should warn |
| A section goes blank after a swap | Targeted block is missing or rendered empty | chirp check; verify the Fragment or Pageblock name |
| OOB update does nothing | hx-swap-oobtarget id does not exist in the current layout |
Check the OOB registry and layout block ids |
| Suspense skeleton never resolves | Deferred block was not discovered or mapped to the wrong target | Checkdefer_blocks, defer_map, and block dependencies |
| Empty list looks like loading | Template used{% if items %}for a deferred value |
Use{% if items is not none %} or __chirp_defer_pending__ |
| SSE stream stops after one bad event | Error boundary widened beyond one event | CheckEventStreamgenerator and fragment render errors |
| Boosted link reloads the page | Link crosses shell boundaries or boost is disabled | CheckHX-Redirect, shell layout domains, and hx-boostinheritance |
| Duplicate shell actions or badges appear | OOB region rendered inline and out-of-band | Register the region and keep the DOM id in one owner |
Kida-powered template diagnostics use their own categories:
| Category | Meaning | Typical fix | |
|---|---|---|---|
component |
A local{% def %}call has unknown, missing, duplicate, or literal type-mismatched arguments |
Fix the call site or the component signature | |
template_context |
An opt-in dotted context contract does not cover what the template reads | Add the missing path toprovided, move it to optional, or stop reading it |
|
template_escape |
A template deliberately trusts markup, such as ` | safe`, and should document the trust boundary | Addsafe(reason="...")or remove the trust override |
template_privacy |
A template reads sensitive-looking data such as tokens, secrets, or password paths | Confirm the value belongs in rendered output or remove it |
For dotted context checks, register template contracts through the existing check-data channel:
app.set_contract_check_data(
"template_context_contracts",
{
"page.html": {
"provided": {"page.title", "user.name"},
"optional": {"flash.message"},
}
},
)
Read The Headers
Whendebug=True, Chirp can expose request and route context through headers:
X-Chirp-Route-KindX-Chirp-Route-FilesX-Chirp-Route-MetaX-Chirp-Route-SectionX-Chirp-Context-ChainX-Chirp-Shell-Context
These are useful when a filesystem route, section, shell mode, or layout chain does not match the route you thought was serving the request.
Keep The Return Type Honest
Most swap bugs reduce to the wrong return type:
| Need | Return |
|---|---|
| Full page only | Template(...) |
| Full page for browsers, fragment for htmx | Page(...) |
| One named block | Fragment(...) |
| Main fragment plus extra regions | OOB(...) |
| Initial shell plus deferred blocks | Suspense(...) |
| Post-load long-lived updates | EventStream(...) |
If the response shape is unclear, start at Return Values.