sveltejs/svelte · warning · MigrationError

Encountered an export declaration pattern that is not suppor

Error message

Encountered an export declaration pattern that is not supported for automigration.

What it means

Thrown when an `export let` declaration uses a destructuring or non-Identifier pattern (e.g. `export let { x, y } = obj` or `export let [a, b] = arr`). The automigrator only knows how to convert simple Identifier exports into `$props()` bindings; turning destructuring into props is non-trivial because the leaves — not the pattern keys — are the prop names. The TODO comment in source confirms this is an intentionally unimplemented case.

Source

Thrown at packages/svelte/src/compiler/migrate/index.js:605

			} catch (e) {
				// no bindings, so we can skip this
				next();
				continue;
			}
			const has_state = bindings.some((binding) => binding.kind === 'state');
			const has_props = bindings.some((binding) => binding.kind === 'bindable_prop');

			if (!has_state && !has_props) {
				next();
				continue;
			}

			if (has_props) {
				nr_of_props++;

				if (declarator.id.type !== 'Identifier') {
					// TODO invest time in this?
					throw new MigrationError(
						'Encountered an export declaration pattern that is not supported for automigration.'
					);
					// Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
					// means that foo and bar are the props (i.e. the leaves are the prop names), not x and z.
					// const tmp = b.id(state.scope.generate('tmp'));
					// const paths = extract_paths(declarator.id, tmp);
					// state.props_pre.push(
					// 	b.declaration('const', tmp, visit(declarator.init!) as Expression)
					// );
					// for (const path of paths) {
					// 	const name = (path.node as Identifier).name;
					// 	const binding = state.scope.get(name)!;
					// 	const value = path.expression;
					// 	if (binding.kind === 'bindable_prop' || binding.kind === 'rest_prop') {
					// 		state.props.push({
					// 			local: name,
					// 			exported: binding.prop_alias ? binding.prop_alias : name,
					// 			init: value

View on GitHub (pinned to 20b341f100)

Solutions

  1. Refactor the destructured `export let` into individual `export let` declarations (one per prop) before migrating.
  2. Migrate the file by hand using `let { theme = $bindable(), size = $bindable() } = $props()`.
  3. If the destructured source is a derived object (not the props bag), split the prop declarations from the destructuring.

Example fix

// before
<script>
  export let { theme, size } = config;
</script>

// after (manual migration)
<script>
  let { theme = $bindable(), size = $bindable() } = $props();
</script>
Defensive patterns

Strategy: validation

Validate before calling

// Detect destructuring export-let patterns before invoking migrate.
function hasDestructuredExport(source) {
  return /export\s+let\s*[\[\{]/.test(source);
}
if (hasDestructuredExport(componentSource)) {
  // refactor to plain `export let x; export let y;` before migrating
}

Try / catch

const { code } = migrate({ filename, source });
if (code.startsWith('<!-- @migration-task') && /export declaration pattern/i.test(code)) {
  // hand-migrate the destructured export let to $props() with $bindable()
}

Prevention

When it happens

Trigger: Running `migrate()` against a Svelte 4 component whose `<script>` contains `export let { ... } = ` or `export let [ ... ] = `, where at least one of the destructured bindings resolves to a bindable prop (`binding.kind === 'bindable_prop'`). The check `declarator.id.type !== 'Identifier'` triggers the throw.

Common situations: Components that destructure config objects as props, e.g. `export let { theme, size } = config`. Common in design-system and UI-kit components written for Svelte 4.

Related errors


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