sveltejs/svelte · warning · MigrationError
can't migrate `: ${...}` to `$${rune}` because there's a var
Error message
can't migrate `: ${...}` to `$${rune}` because there's a variable named ${rune}.
Rename the variable and try again or migrate by hand. What it means
Thrown when migrating a reactive `$:` labeled statement to a rune would collide with a variable named `state` or `derived`. The `$:` body source is shown in the message. Same lexical-collision guard as errors 1 and 5, applied specifically to the `$:` → `$derived`/`$state` rewrite path.
Source
Thrown at packages/svelte/src/compiler/migrate/index.js:941
/** @type {number} */ (node.end),
'return;'
);
},
LabeledStatement(node, { path, state, next }) {
if (state.analysis.runes) return;
if (path.length > 1) return;
if (node.label.name !== '$') return;
if (state.derived_labeled_statements.has(node)) return;
next();
/**
* @param {"state"|"derived"} rune
*/
function check_rune_binding(rune) {
const has_rune_binding = state.scope.get(rune);
if (has_rune_binding) {
throw new MigrationError(
`can't migrate \`$: ${state.str.original.substring(/** @type {number} */ (node.body.start), node.body.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
);
}
}
if (
node.body.type === 'ExpressionStatement' &&
node.body.expression.type === 'AssignmentExpression'
) {
const { left, right } = node.body.expression;
const ids = extract_identifiers(left);
const [, expression_ids] = extract_all_identifiers_from_expression(right);
const bindings = ids.map((id) => /** @type {Binding} */ (state.scope.get(id.name)));
if (bindings.every((b) => b.kind === 'legacy_reactive')) {
if (
right.type !== 'Literal' &&View on GitHub (pinned to 20b341f100)
Solutions
- Rename the colliding `state`/`derived` binding in the component, then re-run migrate.
- Convert the `$:` statement by hand to `$derived(...)` or `$effect(...)` and skip the codemod for that file.
- Grep for `\b(state|derived)\b` declarations in components using `$:` before migrating.
Example fix
// before <script> let state = 0; $: doubled = state * 2; </script> // after (rename, then migrate) <script> let currentState = $state(0); let doubled = $derived(currentState * 2); </script>
Defensive patterns
Strategy: validation
Validate before calling
// Detect `$:` statements in components that also bind `state`/`derived`.
function reactiveLabelWithNameCollision(source) {
return /\$:\s/.test(source) && /\b(let|const|var)\s+(state|derived)\b/.test(source);
}
if (reactiveLabelWithNameCollision(componentSource)) {
// rename the binding before migrating
} Try / catch
const { code } = migrate({ filename, source });
if (code.startsWith('<!-- @migration-task') && /\$\$\{rune\}` because there's a variable named/i.test(code)) {
// hand-convert the `$:` to $derived/$effect after renaming the collision
} Prevention
- Reserve `state` and `derived` as rune identifiers when adopting Svelte 5.
- Audit `$:` blocks in components that also use those identifier names before migrating.
- Prefer converting reactive labels by hand where the body is complex.
When it happens
Trigger: Running `migrate()` on a Svelte 4 component that has both a `$:` reactive statement (being rewritten to `$derived` or `$state`) and a top-level binding literally named `state` or `derived`. The `check_rune_binding` helper at line 936 throws.
Common situations: Reactive components that also use `state`/`derived` as identifiers (state machines, derived-data caches). The `$:` rewrite is the most common trigger because Svelte 4 components lean heavily on reactive labels.
Related errors
- migrating this component would require adding a `$${rune}` r
- can't migrate `${...}` to `$${rune}` because there's a varia
- This migration would change the name of a slot (${slot_name}
- This migration would change the name of a slot (${name} to $
- Can't migrate code with ${illegal_specifiers.join(' and ')}.
AI-assisted analysis of sveltejs/svelte@20b341f100 (2026-08-12).
Data as JSON: /api/errors/a4eedda681579c16.
Report an issue: GitHub.