diff --git a/.changeset/sour-lets-hide.md b/.changeset/sour-lets-hide.md
new file mode 100644
index 0000000000..6175027cff
--- /dev/null
+++ b/.changeset/sour-lets-hide.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: error when using `let:` directives on a component with a `children` snippet
diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md
index 02e762dc48..eea1be14c8 100644
--- a/documentation/docs/98-reference/.generated/compile-errors.md
+++ b/documentation/docs/98-reference/.generated/compile-errors.md
@@ -677,6 +677,12 @@ Cannot use `$$restProps` in runes mode
`let:` directive at invalid position
```
+### let_directive_snippet_conflict
+
+```
+Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ %name% })}`)
+```
+
### mixed_event_handler_syntaxes
```
diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md
index 6d1ae6ccbf..f71119169f 100644
--- a/packages/svelte/messages/compile-errors/template.md
+++ b/packages/svelte/messages/compile-errors/template.md
@@ -279,6 +279,10 @@ The same applies to components:
> `let:` directive at invalid position
+## let_directive_snippet_conflict
+
+> Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ %name% })}`)
+
## mixed_event_handler_syntaxes
> Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax
diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js
index ccb7fe833e..f2be746ff6 100644
--- a/packages/svelte/src/compiler/errors.js
+++ b/packages/svelte/src/compiler/errors.js
@@ -1213,6 +1213,16 @@ export function let_directive_invalid_placement(node) {
e(node, 'let_directive_invalid_placement', `\`let:\` directive at invalid position\nhttps://svelte.dev/e/let_directive_invalid_placement`);
}
+/**
+ * Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ %name% })}`)
+ * @param {null | number | NodeLike} node
+ * @param {string} name
+ * @returns {never}
+ */
+export function let_directive_snippet_conflict(node, name) {
+ e(node, 'let_directive_snippet_conflict', `Cannot use \`let:\` directives on a component that has a \`children\` snippet. Use snippet parameters instead (e.g. \`{#snippet children({ ${name} })}\`)\nhttps://svelte.dev/e/let_directive_snippet_conflict`);
+}
+
/**
* Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax
* @param {null | number | NodeLike} node
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js
index e9a56ead0c..2eccac9d8a 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js
@@ -1,6 +1,7 @@
/** @import { AST } from '#compiler' */
/** @import { Context } from '../types' */
import * as e from '../../../errors.js';
+import { determine_slot } from '../../../utils/slot.js';
/**
* @param {AST.LetDirective} node
@@ -21,4 +22,25 @@ export function LetDirective(node, context) {
) {
e.let_directive_invalid_placement(node);
}
+
+ // an explicit `children` snippet replaces the default slot content, so the `let:`
+ // directive would never be provided. if the component has a `slot` attribute, the
+ // directive applies to the component itself instead, which is fine
+ if (
+ (parent.type === 'Component' ||
+ parent.type === 'SvelteComponent' ||
+ parent.type === 'SvelteSelf') &&
+ !determine_slot(parent) &&
+ parent.fragment.nodes.some(
+ (child) => child.type === 'SnippetBlock' && child.expression.name === 'children'
+ )
+ ) {
+ const { expression } = node;
+ const pattern =
+ expression === null || (expression.type === 'Identifier' && expression.name === node.name)
+ ? node.name
+ : `${node.name}: ${context.state.analysis.source.slice(expression.start, expression.end)}`;
+
+ e.let_directive_snippet_conflict(node, pattern);
+ }
}
diff --git a/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet-expression/_config.js b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet-expression/_config.js
new file mode 100644
index 0000000000..bf56000196
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet-expression/_config.js
@@ -0,0 +1,10 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'let_directive_snippet_conflict',
+ message:
+ 'Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ item: x })}`)',
+ position: [62, 74]
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet-expression/main.svelte b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet-expression/main.svelte
new file mode 100644
index 0000000000..eddd5f2308
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet-expression/main.svelte
@@ -0,0 +1,7 @@
+
+
+
+ {#snippet children()}{x}{/snippet}
+
diff --git a/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet/_config.js b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet/_config.js
new file mode 100644
index 0000000000..27b6a88667
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet/_config.js
@@ -0,0 +1,10 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'let_directive_snippet_conflict',
+ message:
+ 'Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ value })}`)',
+ position: [359, 368]
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet/main.svelte b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet/main.svelte
new file mode 100644
index 0000000000..bdfe3de9b5
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/let-directive-children-snippet/main.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+ {value}
+ {#snippet foo()}x{/snippet}
+
+
+ {#snippet children({ value })}{value}{/snippet}
+
+
+
+ {#snippet children()}{value}{/snippet}
+
+
+
+
+
+ {#snippet children()}Value is: {value}{/snippet}
+