diff --git a/.changeset/nice-pianos-punch.md b/.changeset/nice-pianos-punch.md
new file mode 100644
index 0000000000..f70af9eb04
--- /dev/null
+++ b/.changeset/nice-pianos-punch.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: prevent state runes from being called with spread
diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md
index ea116014e7..a8c39aaf97 100644
--- a/documentation/docs/98-reference/.generated/compile-errors.md
+++ b/documentation/docs/98-reference/.generated/compile-errors.md
@@ -660,6 +660,12 @@ Cannot access a computed property of a rune
`%name%` is not a valid rune
```
+### rune_invalid_spread
+
+```
+`%rune%` cannot be called with a spread argument
+```
+
### rune_invalid_usage
```
diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md
index 795c0b007d..aabcbeae48 100644
--- a/packages/svelte/messages/compile-errors/script.md
+++ b/packages/svelte/messages/compile-errors/script.md
@@ -162,6 +162,10 @@ This turned out to be buggy and unpredictable, particularly when working with de
> `%name%` is not a valid rune
+## rune_invalid_spread
+
+> `%rune%` cannot be called with a spread argument
+
## rune_invalid_usage
> Cannot use `%rune%` rune in non-runes mode
diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js
index 677b99fcff..6bf973948b 100644
--- a/packages/svelte/src/compiler/errors.js
+++ b/packages/svelte/src/compiler/errors.js
@@ -383,6 +383,16 @@ export function rune_invalid_name(node, name) {
e(node, 'rune_invalid_name', `\`${name}\` is not a valid rune\nhttps://svelte.dev/e/rune_invalid_name`);
}
+/**
+ * `%rune%` cannot be called with a spread argument
+ * @param {null | number | NodeLike} node
+ * @param {string} rune
+ * @returns {never}
+ */
+export function rune_invalid_spread(node, rune) {
+ e(node, 'rune_invalid_spread', `\`${rune}\` cannot be called with a spread argument\nhttps://svelte.dev/e/rune_invalid_spread`);
+}
+
/**
* Cannot use `%rune%` rune in non-runes mode
* @param {null | number | NodeLike} node
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js
index 6ef323725b..2eac934b33 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js
@@ -17,6 +17,14 @@ export function CallExpression(node, context) {
const rune = get_rune(node, context.state.scope);
+ if (rune && rune !== '$inspect') {
+ for (const arg of node.arguments) {
+ if (arg.type === 'SpreadElement') {
+ e.rune_invalid_spread(node, rune);
+ }
+ }
+ }
+
switch (rune) {
case null:
if (!is_safe_identifier(node.callee, context.state.scope)) {
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json
new file mode 100644
index 0000000000..be59da95fa
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 35,
+ "line": 3
+ },
+ "message": "`$derived.by` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte
new file mode 100644
index 0000000000..49e8057aa5
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json
new file mode 100644
index 0000000000..6a333bc362
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 32,
+ "line": 3
+ },
+ "message": "`$derived` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte
new file mode 100644
index 0000000000..9155493e17
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json
new file mode 100644
index 0000000000..e08b498fcb
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 34,
+ "line": 3
+ },
+ "message": "`$state.raw` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte
new file mode 100644
index 0000000000..d06fb053b3
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json
new file mode 100644
index 0000000000..11ae2abce5
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json
@@ -0,0 +1,14 @@
+[
+ {
+ "code": "rune_invalid_spread",
+ "end": {
+ "column": 30,
+ "line": 3
+ },
+ "message": "`$state` cannot be called with a spread argument",
+ "start": {
+ "column": 15,
+ "line": 3
+ }
+ }
+]
diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte
new file mode 100644
index 0000000000..02feac893f
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file