sveltejs/svelte · error · Error
effect_in_teardown
effect_in_teardown
Error message
effect_in_teardown
`${rune}` cannot be used inside an effect cleanup function
https://svelte.dev/e/effect_in_teardown What it means
Runtime error `effect_in_teardown`: a Svelte 5 rune (`$effect`, `$pre_effect`, or `$derived`) was used inside an effect cleanup (teardown) function. Effects and deriveds must be created during the setup phase of an owner effect, not during cleanup, because teardowns run in an unowned context. The message names the offending rune.
Source
Thrown at packages/svelte/src/internal/client/errors.js:182
} else {
throw new Error(`https://svelte.dev/e/each_key_volatile`);
}
}
/**
* `%rune%` cannot be used inside an effect cleanup function
* @param {string} rune
* @returns {never}
*/
export function effect_in_teardown(rune) {
if (DEV) {
const error = new Error(`effect_in_teardown\n\`${rune}\` cannot be used inside an effect cleanup function\nhttps://svelte.dev/e/effect_in_teardown`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/effect_in_teardown`);
}
}
/**
* Effect cannot be created inside a `$derived` value that was not itself created inside an effect
* @returns {never}
*/
export function effect_in_unowned_derived() {
if (DEV) {
const error = new Error(`effect_in_unowned_derived\nEffect cannot be created inside a \`$derived\` value that was not itself created inside an effect\nhttps://svelte.dev/e/effect_in_unowned_derived`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/effect_in_unowned_derived`);
}
}View on GitHub (pinned to 20b341f100)
Solutions
- Move the `$effect`/`$derived` creation out of the cleanup and into the effect body (the setup phase).
- For cleanup-only work, do plain imperative teardown (clear timers, remove listeners) without using runes.
- If you need a derived that lives across the effect lifetime, declare it at component scope, not inside the cleanup.
Example fix
// before
$effect(() => {
return () => {
$effect(() => { /* ... */ }); // teardown starting an effect
};
});
// after
$effect(() => {
// setup runs here
$effect(() => { /* ... */ });
return () => {
// cleanup: only plain imperative teardown, no runes
};
}); Defensive patterns
Strategy: validation
Validate before calling
// Detect rune usage inside an $effect cleanup return.
function runeInTeardown(source) {
// crude: finds `$effect(() => { ... return () => { $effect(...)/$derived(...) } })`
return /return\s*\(\s*\(?\)\s*=>\s*\{[\s\S]*?\$(?:effect|derived|pre_effect)\(/.test(source);
} Prevention
- Create runes only in the setup (body) of an `$effect`, never in its returned cleanup.
- Keep teardown callbacks imperative: clear timers, remove listeners, abort fetches.
- Declare cross-effect deriveds at component scope, not inside cleanup closures.
When it happens
Trigger: At runtime (DEV) when the body of an `$effect` cleanup callback (the function returned from the effect, or registered via `$.onDestroy`-style APIs) calls `$effect(...)`, `$derived(...)`, or another lifecycle rune.
Common situations: Migrating Svelte 4 `onDestroy(() => { ... })` callbacks that set up new reactive computations; cleanup functions that try to subscribe to a store or start a timer-driven effect; refactoring that moved setup logic into teardown by mistake.
Related errors
- async_derived_orphan
- effect_orphan
- lifecycle_outside_component
- migrating this component would require adding a `$${rune}` r
- can't migrate `${...}` to `$${rune}` because there's a varia
AI-assisted analysis of sveltejs/svelte@20b341f100 (2026-08-12).
Data as JSON: /api/errors/9728b58ecfe9e864.
Report an issue: GitHub.