Skip to content

fix: silence $inspect errors when the effect is about to be destroyed #16391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/six-swans-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: silence `$inspect` errors when the effect is about to be destroyed
51 changes: 33 additions & 18 deletions packages/svelte/src/internal/client/dev/inspect.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UNINITIALIZED } from '../../../constants.js';
import { snapshot } from '../../shared/clone.js';
import { inspect_effect, validate_effect } from '../reactivity/effects.js';
import { inspect_effect, render_effect, validate_effect } from '../reactivity/effects.js';
import { untrack } from '../runtime.js';

/**
Expand All @@ -12,29 +12,44 @@ export function inspect(get_value, inspector = console.log) {
validate_effect('$inspect');

let initial = true;
let error = /** @type {any} */ (UNINITIALIZED);

// Inspect effects runs synchronously so that we can capture useful
// stack traces. As a consequence, reading the value might result
// in an error (an `$inspect(object.property)` will run before the
// `{#if object}...{/if}` that contains it)
inspect_effect(() => {
/** @type {any} */
var value = UNINITIALIZED;

// Capturing the value might result in an exception due to the inspect effect being
// sync and thus operating on stale data. In the case we encounter an exception we
// can bail-out of reporting the value. Instead we simply console.error the error
// so at least it's known that an error occured, but we don't stop execution
try {
value = get_value();
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
var value = get_value();
} catch (e) {
error = e;
return;
}

if (value !== UNINITIALIZED) {
var snap = snapshot(value, true);
untrack(() => {
inspector(initial ? 'init' : 'update', ...snap);
});
}
var snap = snapshot(value, true);
untrack(() => {
inspector(initial ? 'init' : 'update', ...snap);
});

initial = false;
});

// If an error occurs, we store it (along with its stack trace).
// If the render effect subsequently runs, we log the error,
// but if it doesn't run it's because the `$inspect` was
// destroyed, meaning we don't need to bother
render_effect(() => {
try {
// call `get_value` so that this runs alongside the inspect effect
get_value();
} catch {
// ignore
}

if (error !== UNINITIALIZED) {
// eslint-disable-next-line no-console
console.error(error);
error = UNINITIALIZED;
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default test({
b1?.click();
flushSync();

assert.ok(errors.length > 0);
assert.equal(errors.length, 0);
assert.deepEqual(logs, ['init', 'a', 'init', 'b']);
}
});
Loading