Skip to content

Log the component stack of errors no boundary handles - #5297

Merged
JoviDeCroock merged 4 commits into
mainfrom
JoviDeCroock/debug-uncaught-component-stack
Oct 7, 2026
Merged

JoviDeCroock merged 4 commits into
mainfrom
JoviDeCroock/debug-uncaught-component-stack

Conversation

@JoviDeCroock

@JoviDeCroock JoviDeCroock commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

Refs #2323

When an error escapes because no boundary handles it, preact/debug now logs which component threw it and its component stack:

An error occurred in the <Thrower> component, and no error boundary handled it.

  in Thrower (at src/Thrower.jsx:3)
  in Foo (at src/App.jsx:12)

Until now the stack was only attached to the errorInfo that boundaries receive, so an uncaught error showed up with a stack that only pointed into Preact. React 19 logs the same information in development.

The message is logged right after the error has left Preact, once per escape. An error that passes through several components, gets rethrown by a boundary, or is caught by an outer tree around a nested render() is reported once or not at all. Refs and unmount cleanups name the component they belong to, and a missing Suspense boundary names the component that suspended. Caught errors are logged as before.

It also fixes debug's render and owner stacks, which went stale after a render threw and drifted on every DOM element because diffed popped for every vnode. captureOwnerStack() returned leftover components outside render, and later stacks included components from the failed render. Each component now restores the stack lengths from before it was diffed.

This does not change where the debugger pauses. options._catchError runs after the error has been caught, so preact/debug can't restore "pause on uncaught exceptions"; React 19 no longer pauses there either.

@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Size Change: +300 B (+1.88%)

Total Size: 16.3 kB

📦 View Changed
Filename Size Change
debug/dist/debug.mjs 4.2 kB +300 B (+7.69%) 🔍
ℹ️ View Unchanged
Filename Size
compat/dist/compat.mjs 3.95 kB
devtools/dist/devtools.mjs 268 B
dist/preact.mjs 4.93 kB
hooks/dist/hooks.mjs 1.58 kB
jsx-runtime/dist/jsxRuntime.mjs 868 B
test-utils/dist/testUtils.mjs 473 B

compressed-size-action

@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

📊 Tachometer Benchmark Results

Summary

⏳ Benchmarks are currently running. Results below are out of date.

duration

  • create10k: unsure 🔍 -1% - +0% (-6.69ms - +3.32ms)
    preact-local vs preact-main
  • filter-list: unsure 🔍 -0% - +0% (-0.06ms - +0.06ms)
    preact-local vs preact-main
  • hydrate1k: unsure 🔍 -1% - +1% (-0.83ms - +0.84ms)
    preact-local vs preact-main
  • many-updates: unsure 🔍 -0% - +0% (-0.05ms - +0.01ms)
    preact-local vs preact-main
  • replace1k: unsure 🔍 -0% - +2% (-0.17ms - +1.46ms)
    preact-local vs preact-main
  • text-update: unsure 🔍 -2% - +8% (-0.03ms - +0.13ms)
    preact-local vs preact-main
  • todo: unsure 🔍 -1% - +1% (-0.34ms - +0.29ms)
    preact-local vs preact-main
  • update10th1k: unsure 🔍 -4% - +5% (-1.24ms - +1.64ms)
    preact-local vs preact-main

usedJSHeapSize

  • create10k: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • filter-list: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • hydrate1k: unsure 🔍 -4% - +6% (-0.26ms - +0.34ms)
    preact-local vs preact-main
  • many-updates: unsure 🔍 -0% - +0% (-0.00ms - +0.01ms)
    preact-local vs preact-main
  • replace1k: unsure 🔍 +0% - +0% (+0.00ms - +0.00ms)
    preact-local vs preact-main
  • text-update: slower ❌ 0% - 7% (0.00ms - 0.08ms)
    preact-local vs preact-main
  • todo: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • update10th1k: unsure 🔍 -0% - +0% (-0.01ms - +0.01ms)
    preact-local vs preact-main

Results

⏳ Benchmarks are currently running. Results below are out of date.
create10k

duration

VersionAvg timevs preact-localvs preact-main
preact-local814.66ms - 820.99ms-unsure 🔍
-1% - +0%
-6.69ms - +3.32ms
preact-main815.63ms - 823.39msunsure 🔍
-0% - +1%
-3.32ms - +6.69ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local19.08ms - 19.08ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main19.08ms - 19.08msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
filter-list

duration

VersionAvg timevs preact-localvs preact-main
preact-local16.51ms - 16.56ms-unsure 🔍
-0% - +0%
-0.06ms - +0.06ms
preact-main16.48ms - 16.59msunsure 🔍
-0% - +0%
-0.06ms - +0.06ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.56ms - 1.56ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main1.56ms - 1.56msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
hydrate1k

duration

VersionAvg timevs preact-localvs preact-main
preact-local56.95ms - 58.22ms-unsure 🔍
-1% - +1%
-0.83ms - +0.84ms
preact-main57.05ms - 58.12msunsure 🔍
-1% - +1%
-0.84ms - +0.83ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local5.79ms - 6.19ms-unsure 🔍
-4% - +6%
-0.26ms - +0.34ms
preact-main5.73ms - 6.17msunsure 🔍
-6% - +4%
-0.34ms - +0.26ms
-
many-updates

duration

VersionAvg timevs preact-localvs preact-main
preact-local16.52ms - 16.56ms-unsure 🔍
-0% - +0%
-0.05ms - +0.01ms
preact-main16.54ms - 16.58msunsure 🔍
-0% - +0%
-0.01ms - +0.05ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local3.75ms - 3.75ms-unsure 🔍
-0% - +0%
-0.00ms - +0.01ms
preact-main3.75ms - 3.75msunsure 🔍
-0% - +0%
-0.01ms - +0.00ms
-
replace1k
  • Browser: chrome-headless
  • Sample size: 100
  • Built by: CI #6064
  • Commit: a40cdf6

duration

VersionAvg timevs preact-localvs preact-main
preact-local91.05ms - 92.12ms-unsure 🔍
-0% - +2%
-0.17ms - +1.46ms
preact-main90.33ms - 91.55msunsure 🔍
-2% - +0%
-1.46ms - +0.17ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local3.02ms - 3.02ms-unsure 🔍
+0% - +0%
+0.00ms - +0.00ms
preact-main3.02ms - 3.02msunsure 🔍
-0% - -0%
-0.00ms - -0.00ms
-

run-warmup-0

VersionAvg timevs preact-localvs preact-main
preact-local27.76ms - 28.66ms-unsure 🔍
-2% - +3%
-0.51ms - +0.71ms
preact-main27.69ms - 28.52msunsure 🔍
-3% - +2%
-0.71ms - +0.51ms
-

run-warmup-1

VersionAvg timevs preact-localvs preact-main
preact-local32.30ms - 33.40ms-unsure 🔍
-3% - +2%
-1.03ms - +0.55ms
preact-main32.52ms - 33.67msunsure 🔍
-2% - +3%
-0.55ms - +1.03ms
-

run-warmup-2

VersionAvg timevs preact-localvs preact-main
preact-local32.82ms - 34.33ms-unsure 🔍
-3% - +3%
-1.14ms - +0.94ms
preact-main32.96ms - 34.40msunsure 🔍
-3% - +3%
-0.94ms - +1.14ms
-

run-warmup-3

VersionAvg timevs preact-localvs preact-main
preact-local31.09ms - 31.81ms-unsure 🔍
-1% - +3%
-0.32ms - +0.92ms
preact-main30.65ms - 31.65msunsure 🔍
-3% - +1%
-0.92ms - +0.32ms
-

run-warmup-4

VersionAvg timevs preact-localvs preact-main
preact-local20.86ms - 21.09ms-unsure 🔍
-1% - +1%
-0.12ms - +0.20ms
preact-main20.82ms - 21.04msunsure 🔍
-1% - +1%
-0.20ms - +0.12ms
-

run-final

VersionAvg timevs preact-localvs preact-main
preact-local27.32ms - 27.75ms-unsure 🔍
-0% - +2%
-0.13ms - +0.44ms
preact-main27.20ms - 27.57msunsure 🔍
-2% - +0%
-0.44ms - +0.13ms
-
text-update
  • Browser: chrome-headless
  • Sample size: 230
  • Built by: CI #6064
  • Commit: a40cdf6

duration

VersionAvg timevs preact-localvs preact-main
preact-local1.65ms - 1.76ms-unsure 🔍
-2% - +8%
-0.03ms - +0.13ms
preact-main1.60ms - 1.71msunsure 🔍
-7% - +2%
-0.13ms - +0.03ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.07ms - 1.13ms-slower ❌
0% - 7%
0.00ms - 0.08ms
preact-main1.03ms - 1.08msfaster ✔
0% - 7%
0.00ms - 0.08ms
-
todo

duration

VersionAvg timevs preact-localvs preact-main
preact-local31.34ms - 31.72ms-unsure 🔍
-1% - +1%
-0.34ms - +0.29ms
preact-main31.30ms - 31.80msunsure 🔍
-1% - +1%
-0.29ms - +0.34ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.28ms - 1.28ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main1.28ms - 1.28msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
update10th1k

duration

VersionAvg timevs preact-localvs preact-main
preact-local33.54ms - 35.80ms-unsure 🔍
-4% - +5%
-1.24ms - +1.64ms
preact-main33.58ms - 35.36msunsure 🔍
-5% - +4%
-1.64ms - +1.24ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local2.97ms - 2.98ms-unsure 🔍
-0% - +0%
-0.01ms - +0.01ms
preact-main2.97ms - 2.98msunsure 🔍
-0% - +0%
-0.01ms - +0.01ms
-

tachometer-reporter-action v2 for CI

@JoviDeCroock
JoviDeCroock marked this pull request as ready for review October 4, 2026 05:53
@coveralls

coveralls commented Oct 4, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 99.856% (+0.004%) from 99.852% — JoviDeCroock/debug-uncaught-component-stack into main

@JoviDeCroock
JoviDeCroock merged commit f9facd3 into main Oct 7, 2026
13 checks passed
@JoviDeCroock
JoviDeCroock deleted the JoviDeCroock/debug-uncaught-component-stack branch October 7, 2026 09:39
@JoviDeCroock JoviDeCroock mentioned this pull request Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants