From fe4a56b0a8b065777c4f01dab63e4fbd260ef758 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gautier=20Ben=20A=C3=AFm?=
<48261497+GauBen@users.noreply.github.com>
Date: Wed, 19 Aug 2026 16:06:15 +0200
Subject: [PATCH] fix: avoid double-calling a derived reference when
destructuring (#18668)
Hi! This PR fixes #18666
### Before submitting the PR, please make sure you do the following
- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).
### Tests and linting
- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
---
.changeset/nervous-dolls-clean.md | 5 +++++
.../3-transform/server/visitors/VariableDeclaration.js | 6 ++++--
.../samples/derived-destructured-from-derived/_config.js | 5 +++++
.../samples/derived-destructured-from-derived/main.svelte | 8 ++++++++
4 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 .changeset/nervous-dolls-clean.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/main.svelte
diff --git a/.changeset/nervous-dolls-clean.md b/.changeset/nervous-dolls-clean.md
new file mode 100644
index 0000000000..5b31bdd3c7
--- /dev/null
+++ b/.changeset/nervous-dolls-clean.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: avoid double-calling a derived reference when destructuring `$derived` of another `$derived` during server-side rendering
diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js
index 2c7b14afa5..108f65a884 100644
--- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js
+++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js
@@ -102,9 +102,11 @@ export function VariableDeclaration(node, context) {
} else {
const call = /** @type {CallExpression} */ (declarator.init);
- let rhs = value;
+ // - cannot be a SpreadElement because refused during analysis
+ // - use args[0] rather than value to avoid visiting twice (above in const value = ... and below in for-ofs)
+ let rhs = /** @type {Expression} */ (call.arguments[0]);
- if (rune !== '$derived' || call.arguments[0].type !== 'Identifier') {
+ if (rune === '$derived.by' || call.arguments[0].type !== 'Identifier') {
const id = b.id(context.state.scope.generate('$$d'));
rhs = b.call(id);
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/_config.js
new file mode 100644
index 0000000000..a36c2adf7d
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/_config.js
@@ -0,0 +1,5 @@
+import { test } from '../../test';
+
+export default test({
+ html: ``
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/main.svelte
new file mode 100644
index 0000000000..b1882243f4
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/derived-destructured-from-derived/main.svelte
@@ -0,0 +1,8 @@
+
+
+
+