Error handling¶
Catch errors raised while rendering with Errored, show a fallback, and recover with a reset callback or automatically when a route changes.
from wybthon import Errored, Prop, Props, button, component, create_signal, div, p, prop, render
from wybthon.router import current_path
class RiskyPanelProps(Props):
should_fail: Prop[bool] = prop(default=False)
@component
def RiskyPanel(props: RiskyPanelProps):
# A hole that raises routes the error to the nearest Errored boundary.
def body():
if props.should_fail():
raise RuntimeError("the panel exploded")
return "Everything is fine."
return p(body)
def fallback(err, reset):
return div(
p("Caught: ", lambda: str(err())),
button("Retry", on_click=reset),
style={"color": "crimson"},
)
@component
def App():
fail, set_fail = create_signal(False)
return div(
button("Toggle failure", on_click=lambda: set_fail(lambda v: not v)),
Errored(
lambda: RiskyPanel(should_fail=fail),
fallback=fallback,
on_error=lambda err: print("logged:", err),
reset_on=current_path,
),
)
render(App(), "#app")
How it works¶
Erroredinstalls an error handler on its owner scope. Errors raised in a descendant hole, component body, effect, or async memo route to the nearest boundary; sibling trees are untouched.fallbackmay be a VNode, a string, or a callable. A callable receives(err, reset)(or just(err)).erris an accessor for the caught exception, as in Solid 2.0, so read it witherr()inside a hole. Callingreset()clears the error and re-renders the children; it takes no arguments, so it works directly as a click handler.on_erroris a plain callback for logging or reporting. It receives the exception itself.reset_onis an accessor whose change clears the error automatically. Passingcurrent_pathfromwybthon.routerresets the boundary on every navigation, which is the usual behavior for a page-level boundary.- Boundaries also heal on their own: the boundary remembers which reactive inputs the failing computation read, so toggling the failure off re-renders
RiskyPanelwithout a click on "Retry."
Errors in async data¶
An async memo that rejects raises into the boundary as well. Nest Errored outside Loading so the fallback replaces the pending UI:
from wybthon import Errored, Loading, button, component, create_memo, div, p
async def load_profile():
response = await fetch_json("/api/profile")
if response is None:
raise LookupError("profile not found")
return response
@component
def Profile():
profile = create_memo(load_profile)
return Errored(
lambda: Loading(lambda: p(lambda: profile()["name"]), fallback=p("Loading...")),
fallback=lambda err, reset: div(p(lambda: str(err())), button("Try again", on_click=reset)),
)
Errors in effects¶
create_effect accepts an error= handler that receives exceptions from its compute stage instead of routing them to the boundary:
from wybthon import create_effect
create_effect(
lambda: parse(raw_input()),
lambda parsed: show(parsed),
error=lambda exc: print("parse failed:", exc),
)
Event handlers run outside rendering, so an exception in an on_click handler is logged to the console and doesn't involve any boundary; the UI stays intact.
Next steps¶
- Read the Error Boundaries concept page.
- See the
error_boundaryAPI reference. - Combine with Loading for async failure handling.