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

  1. Rename the colliding `state`/`derived` binding in the component, then re-run migrate.
  2. Convert the `$:` statement by hand to `$derived(...)` or `$effect(...)` and skip the codemod for that file.
  3. 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

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


AI-assisted analysis of sveltejs/svelte@20b341f100 (2026-08-12). Data as JSON: /api/errors/a4eedda681579c16. Report an issue: GitHub.