diff --git a/.changeset/healthy-carpets-deny.md b/.changeset/healthy-carpets-deny.md
new file mode 100644
index 0000000000..94ee865fe7
--- /dev/null
+++ b/.changeset/healthy-carpets-deny.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: move ownership validation into async component body
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
index c42d1b95d8..7f25e6c0d2 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
@@ -364,6 +364,12 @@ export function client_component(analysis, options) {
: b.stmt(b.call('$.init', analysis.immutable ? b.true : undefined))
]);
+ if (analysis.needs_mutation_validation) {
+ component_block.body.unshift(
+ b.var('$$ownership_validator', b.call('$.create_ownership_validator', b.id('$$props')))
+ );
+ }
+
const should_inject_context =
dev ||
analysis.needs_context ||
@@ -434,12 +440,6 @@ export function client_component(analysis, options) {
);
}
- if (analysis.needs_mutation_validation) {
- component_block.body.unshift(
- b.var('$$ownership_validator', b.call('$.create_ownership_validator', b.id('$$props')))
- );
- }
-
// we want the cleanup function for the stores to run as the very last thing
// so that it can effectively clean up the store subscription even after the user effects runs
if (should_inject_context) {
diff --git a/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/Child.svelte
new file mode 100644
index 0000000000..d4d5cf7554
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/Child.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/_config.js b/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/_config.js
new file mode 100644
index 0000000000..167eee8488
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/_config.js
@@ -0,0 +1,22 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ compileOptions: {
+ dev: true
+ },
+
+ async test({ assert, target, warnings }) {
+ await tick();
+
+ const [button] = target.querySelectorAll('button');
+
+ button.click();
+ await tick();
+
+ assert.htmlEqual(target.innerHTML, '');
+ assert.deepEqual(warnings, [
+ 'Mutating unbound props (`object`, at Child.svelte:7:23) is strongly discouraged. Consider using `bind:object={...}` in main.svelte (or using a callback) instead'
+ ]);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/main.svelte
new file mode 100644
index 0000000000..ae6b43cbb1
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-ownership-validation/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
+ {#snippet pending()}
+ loading...
+ {/snippet}
+