CORRECT006 · Orphan $effect
Severity: critical · Category: correctness
What it checks
Section titled “What it checks”Flags $effect / $effect.pre calls that are guaranteed to run outside component initialisation, so they throw Svelte’s effect_orphan error at runtime:
- A top-level effect in a
.svelte.ts/.svelte.jsrunes module or in a.svelte<script module>block — it runs when the module is imported, outside any component’s initialisation. - A module-scope
newof a class declared in the same file whose constructor creates a bare$effect(one not wrapped in$effect.root) — the shared-state-manager pattern. The finding points at thenewsite.
Not flagged: effects inside functions (including factory functions and IIFEs), effects inside an $effect.root(...) callback, classes that are only instantiated inside components, classes imported from another file, effects in class field initializers or static blocks, and effects in anonymous class expressions (const Store = class { … }). Detection never crosses a function boundary, so it has no false positives by construction — at the cost of missing cross-file and factory variants.
A conditionally-guarded effect — behind a top-level if, or behind a constructor-argument check (constructor(persist) { if (persist) $effect(...) }) — is still flagged even if the guard is never true at runtime, because the guard can’t be evaluated statically. Use an inline suppression (svelte-vitals-disable-next-line CORRECT006) if the guard is intentional.
Why it matters
Section titled “Why it matters”The Svelte compiler compiles all of these patterns without a warning; the failure is runtime-only. In development it can go unnoticed (the module may only be imported on certain routes), and in production it surfaces as a crash — typically a 500 on every page that imports the module. Reactive effects can only be created while a component is initialising, or inside an explicit $effect.root scope.
How to fix
Section titled “How to fix”class QuizStateManager { bookmarks = $state<string[]>([]); constructor() { // ❌ effect_orphan at runtime — no component context at module scope $effect(() => { saveToStorage(this.bookmarks); }); }}export const quizState = new QuizStateManager();Either create a standalone reactive scope with $effect.root — fine when the effect should live for the whole app; own the returned cleanup function if it shouldn’t:
constructor() { $effect.root(() => { $effect(() => { saveToStorage(this.bookmarks); }); });}Or set the effect up during component initialisation instead:
class QuizStateManager { bookmarks = $state<string[]>([]); startPersisting() { $effect(() => { saveToStorage(this.bookmarks); }); }}export const quizState = new QuizStateManager();<script> import { quizState } from '$lib/store.svelte.js'; quizState.startPersisting();</script>