Skip to content

ErrorBoundary renders the caught error's message and stack directly to the DOM, with no recovery action #250

Description

@einari

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions