Skip to content

CatchBoundary can't represent thrown falsy values #8098

Description

@antur84

Which project does this relate to?

Router

Describe the bug

#7753 and #7457 were closed by removing internal throw undefined sites (#7805), and #7910 tracks another one but the reason each of these was catastrophic (whole-app unmount, stackless Uncaught undefined) rather than a rendered errorComponent is still present: CatchBoundary stores the caught value and gates both reset and render on its truthiness:

static getDerivedStateFromError(error) { return { error }; }
// render():
if (error) { /* render errorComponent */ }
return this.props.children;

A thrown falsy value (undefined, null, 0, "") is caught and stored, the gate is false, the boundary re-renders the same crashing children, and React promotes it to an uncaught root error — bypassing the route's errorComponent entirely.

Any userland falsy throw reproduces it on latest; fixing the gate also makes future internal regressions of the #7753/#7910 class degrade gracefully instead of unmounting the app.

Repro (confirmed on @tanstack/react-router 1.170.29 / react 19.2.8): https://github.com/antur84/tanstack-router-falsy-error-boundary-repro.

Suggested fix: track hasError in state separately from the value so the gates don't depend on truthiness (the default ErrorComponent also needs an error?.message guard).

Complete minimal reproducer

https://github.com/antur84/tanstack-router-falsy-error-boundary-repro

Steps to Reproduce the Bug

npm install && node repro.mjs

throw new Error(...): errorComponent rendered = true, escalated to onUncaughtError = false
throw undefined : errorComponent rendered = false, escalated to onUncaughtError = true

Expected: the route errorComponent renders for any thrown value.
Actual: thrown falsy values skip it and surface as uncaught root errors.

Expected behavior

Expected: the route errorComponent renders for any thrown value.

Screenshots or Videos

No response

Platform

  • Router / Start Version: 1.170.29
  • OS: macOS 26.5.2
  • Browser: n/a (headless repro — Node + jsdom)
  • Browser Version: n/a (Node v24.14.1, jsdom 29.1.1)
  • Bundler: none (plain Node ESM)
  • Bundler Version: n/a

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions