From 572444a6961ca73b0972280b9c955c74a02f15a4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Apr 2026 01:20:43 +0200 Subject: [PATCH] fix: ignore false-positive errors of `$inspect` dependencies (#18106) We had logic in place to ignore errors of `$inspect` effects that are about to destroy, but we didn't take into account that we can get these transient errors while checking for `is_dirty` in preparation for running the effect, too. Now effects are marked as dirty in case an error occurs while evaluating their dependencies, which guarantees we will see the error again but we can then handle it properly. Fixes #15741 --------- Co-authored-by: Rich Harris --- .changeset/twelve-cooks-speak.md | 5 +++++ .../svelte/src/internal/client/dev/inspect.js | 2 ++ .../src/internal/client/reactivity/sources.js | 19 ++++++++++++++++- packages/svelte/tests/helpers.js | 2 +- .../inspect-derived-if-destroy/List.svelte | 11 ++++++++++ .../inspect-derived-if-destroy/_config.js | 21 +++++++++++++++++++ .../inspect-derived-if-destroy/main.svelte | 15 +++++++++++++ 7 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 .changeset/twelve-cooks-speak.md create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/List.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/main.svelte diff --git a/.changeset/twelve-cooks-speak.md b/.changeset/twelve-cooks-speak.md new file mode 100644 index 0000000000..d4fcd5c339 --- /dev/null +++ b/.changeset/twelve-cooks-speak.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ignore false-positive errors of `$inspect` dependencies diff --git a/packages/svelte/src/internal/client/dev/inspect.js b/packages/svelte/src/internal/client/dev/inspect.js index 75b29ce9b1..7a8fa0e963 100644 --- a/packages/svelte/src/internal/client/dev/inspect.js +++ b/packages/svelte/src/internal/client/dev/inspect.js @@ -20,6 +20,8 @@ export function inspect(get_value, inspector, show_stack = false) { // in an error (an `$inspect(object.property)` will run before the // `{#if object}...{/if}` that contains it) eager_effect(() => { + error = UNINITIALIZED; + try { var value = get_value(); } catch (e) { diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index a5dd94badf..f374f6a26b 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -266,7 +266,24 @@ export function flush_eager_effects() { eager_effects_deferred = false; for (const effect of eager_effects) { - if (is_dirty(effect)) { + // Mark clean inspect-effects as maybe dirty and then check their dirtiness + // instead of just updating the effects - this way we avoid overfiring. + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + + let dirty; + + try { + dirty = is_dirty(effect); + } catch { + // Dirty-checking can evaluate derived dependencies and throw in cases where + // parent effects are about to destroy this eager effect. Run the effect so + // its own error handling can deal with transient failures. + dirty = true; + } + + if (dirty) { update_effect(effect); } } diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index d0ec8b6e44..52bd47dfae 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -201,7 +201,7 @@ export const async_mode = process.env.SVELTE_NO_ASYNC !== 'true'; * @param {any[]} logs */ export function normalise_inspect_logs(logs) { - /** @type {string[]} */ + /** @type {any[]} */ const normalised = []; for (const log of logs) { diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/List.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/List.svelte new file mode 100644 index 0000000000..c73ac9a99d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/List.svelte @@ -0,0 +1,11 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/_config.js new file mode 100644 index 0000000000..c29022c9dc --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/_config.js @@ -0,0 +1,21 @@ +import { normalise_inspect_logs } from '../../../helpers'; +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + compileOptions: { + dev: true + }, + + async test({ assert, target, errors, logs }) { + const button = target.querySelector('button'); + + flushSync(() => { + button?.click(); + }); + + assert.htmlEqual(target.innerHTML, ''); + assert.equal(errors.length, 0); + assert.deepEqual(normalise_inspect_logs(logs), [[{ id: 1 }, { id: 2 }]]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/main.svelte new file mode 100644 index 0000000000..89e09f417b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-derived-if-destroy/main.svelte @@ -0,0 +1,15 @@ + + +{#if data} + t)} /> +{/if} + +