What happened
@cratis/components' Common/ErrorBoundary (Source/ErrorBoundary.tsx in this repo, published as part of @cratis/components) catches a descendant's render error and renders the raw error diagnostics straight into the page:
render() {
if (this.state.hasError) {
return (
<div className='cratis:p-4'>
<h1 className='cratis:text-3xl cratis:m-3'>Error</h1>
<p>{this.state.error.message}</p>
<p>{this.state.error.stack}</p>
</div>
);
}
return this.props.children;
}
What I expected instead
A component library's shipped error boundary to be safe to use in front of end users by default: no error message, no stack trace, and some way to recover (a reload/retry action) rather than a permanently stuck diagnostic screen.
Why it matters
- Diagnostic/PII leakage.
error.message and error.stack can easily contain request state, identifiers, or file-path/environment details that should never reach an end user's screen. A component library's default should not make that the easy path — an app that just wraps <ErrorBoundary> around a feature (exactly what the doc comment recommends) ships this by default with no opt-out.
- No recovery. There's no action offered — no reload, no reset — so the fallback is a dead end for the person using the app, not merely a diagnostic view for a developer.
- Discovered while implementing Cratis/Ante#21 (locale/branding/render-recovery coordination), which requires "a minimal render-error boundary... offering safe-language reload/recovery without personal diagnostic data." We could not use
@cratis/components' ErrorBoundary for that requirement and built Ante's own (Source/Ante/RenderRecovery/RenderRecoveryBoundary.tsx) instead, specifically because this one violates both of those constraints (it leaks diagnostics and has no recovery action).
Suggested direction (not prescriptive)
- Never render
error.message/error.stack by default; keep componentDidCatch's console.error (that's the right place for it) but keep the rendered fallback generic.
- Accept an optional
fallback render-prop/children-function so a consumer can customize the message and offer a page-specific recovery action, with a sane generic default (a neutral message + a reload/retry button) when none is supplied.
Filed per this project's "register problems where they get fixed" policy — happy to be pointed at an existing design discussion if this is already being reworked.
What happened
@cratis/components'Common/ErrorBoundary(Source/ErrorBoundary.tsxin this repo, published as part of@cratis/components) catches a descendant's render error and renders the raw error diagnostics straight into the page:What I expected instead
A component library's shipped error boundary to be safe to use in front of end users by default: no error message, no stack trace, and some way to recover (a reload/retry action) rather than a permanently stuck diagnostic screen.
Why it matters
error.messageanderror.stackcan easily contain request state, identifiers, or file-path/environment details that should never reach an end user's screen. A component library's default should not make that the easy path — an app that just wraps<ErrorBoundary>around a feature (exactly what the doc comment recommends) ships this by default with no opt-out.@cratis/components'ErrorBoundaryfor that requirement and built Ante's own (Source/Ante/RenderRecovery/RenderRecoveryBoundary.tsx) instead, specifically because this one violates both of those constraints (it leaks diagnostics and has no recovery action).Suggested direction (not prescriptive)
error.message/error.stackby default; keepcomponentDidCatch'sconsole.error(that's the right place for it) but keep the rendered fallback generic.fallbackrender-prop/children-function so a consumer can customize the message and offer a page-specific recovery action, with a sane generic default (a neutral message + a reload/retry button) when none is supplied.Filed per this project's "register problems where they get fixed" policy — happy to be pointed at an existing design discussion if this is already being reworked.