Describe the bug
Looks like this was introduced in 5.55.6.
Used claude for the research/repro:
When an async $derived rejects inside a svelte:boundary that handles the error (via failed and/or onerror), the error is delivered twice. The first delivery is handled correctly — the boundary tears down its main effect and renders the failed snippet. But a follow-up settle then re-runs the reaction (finish in reactivity/async.js), which synchronously re-throws the rejected value and routes it through invoke_error_boundary(error, parent). By then the handling boundary's effect has been destroyed, so effect.b is null, and invoke_error_boundary crashes at:
// packages/svelte/src/internal/client/error-handling.js
/** @type {Boundary} */ (effect.b).error(error); // effect.b is null
with TypeError: Cannot read properties of null (reading 'error'). That secondary error has no live boundary to catch it, so it escapes to the parent boundary — i.e. a child component's localized error takes down a much larger part of the tree.
A dependent $derived that reads the awaited value (const model = $derived({ title: data.title })) is what triggers the re-run; without the second read the reaction has nothing to re-evaluate.
Reproduction
https://svelte.dev/playground/ab1d596e8f5f45ebb7f844db875fe56b?version=5.55.6
Not fixed in 5.56.2 https://svelte.dev/playground/ab1d596e8f5f45ebb7f844db875fe56b?version=5.56.2
Works in 5.55.5 https://svelte.dev/playground/ab1d596e8f5f45ebb7f844db875fe56b?version=5.55.5
Logs
System Info
svelte: 5.55.10
@sveltejs/kit: 2.61.1
Severity
blocking an upgrade
Describe the bug
Looks like this was introduced in 5.55.6.
Used claude for the research/repro:
When an async $derived rejects inside a svelte:boundary that handles the error (via failed and/or onerror), the error is delivered twice. The first delivery is handled correctly — the boundary tears down its main effect and renders the failed snippet. But a follow-up settle then re-runs the reaction (finish in reactivity/async.js), which synchronously re-throws the rejected value and routes it through invoke_error_boundary(error, parent). By then the handling boundary's effect has been destroyed, so effect.b is null, and invoke_error_boundary crashes at:
with TypeError: Cannot read properties of null (reading 'error'). That secondary error has no live boundary to catch it, so it escapes to the parent boundary — i.e. a child component's localized error takes down a much larger part of the tree.
A dependent $derived that reads the awaited value (const model = $derived({ title: data.title })) is what triggers the re-run; without the second read the reaction has nothing to re-evaluate.
Reproduction
https://svelte.dev/playground/ab1d596e8f5f45ebb7f844db875fe56b?version=5.55.6
Not fixed in 5.56.2 https://svelte.dev/playground/ab1d596e8f5f45ebb7f844db875fe56b?version=5.56.2
Works in 5.55.5 https://svelte.dev/playground/ab1d596e8f5f45ebb7f844db875fe56b?version=5.55.5
Logs
System Info
Severity
blocking an upgrade