diff --git a/.changeset/fix-boundary-null.md b/.changeset/fix-boundary-null.md new file mode 100644 index 0000000000..c87a47ce34 --- /dev/null +++ b/.changeset/fix-boundary-null.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ignore errors that occur in destroyed effects diff --git a/packages/svelte/src/internal/client/error-handling.js b/packages/svelte/src/internal/client/error-handling.js index 5d16759266..7c404dd933 100644 --- a/packages/svelte/src/internal/client/error-handling.js +++ b/packages/svelte/src/internal/client/error-handling.js @@ -3,7 +3,7 @@ import { DEV } from 'esm-env'; import { FILENAME } from '../../constants.js'; import { is_firefox } from './dom/operations.js'; -import { ERROR_VALUE, BOUNDARY_EFFECT, REACTION_RAN, EFFECT } from './constants.js'; +import { ERROR_VALUE, BOUNDARY_EFFECT, REACTION_RAN, EFFECT, DESTROYED } from './constants.js'; import { define_property, get_descriptor } from '../shared/utils.js'; import { active_effect, active_reaction } from './runtime.js'; @@ -45,6 +45,10 @@ export function handle_error(error) { * @param {Effect | null} effect */ export function invoke_error_boundary(error, effect) { + if (effect !== null && (effect.f & DESTROYED) !== 0) { + return; + } + while (effect !== null) { if ((effect.f & BOUNDARY_EFFECT) !== 0) { if ((effect.f & REACTION_RAN) === 0) { diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 988998d067..fa6f9dda39 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -180,4 +180,3 @@ export { } from '../shared/validate.js'; export { strict_equals, equals } from './dev/equality.js'; export { log_if_contains_state } from './dev/console-log.js'; -export { invoke_error_boundary } from './error-handling.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/Child.svelte new file mode 100644 index 0000000000..8bfc51bb7a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/Child.svelte @@ -0,0 +1,6 @@ + + +

{model.title}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/_config.js b/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/_config.js new file mode 100644 index 0000000000..a3cda2f865 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/_config.js @@ -0,0 +1,13 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const [button] = target.querySelectorAll('button'); + + button.click(); + await tick(); + + assert.htmlEqual(target.innerHTML, '

error was contained

'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/main.svelte new file mode 100644 index 0000000000..3b5f51742a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-error-boundary-4/main.svelte @@ -0,0 +1,27 @@ + + + + + + {#if open} + + + + {#snippet pending()} +

loading…

+ {/snippet} + + {#snippet failed()} +

error was contained

+ {/snippet} +
+ {/if} + + {#snippet failed()} +

error escaped containment

+ {/snippet} +