#11836·biome

`check --write` rewrites `{@const x = y}` into invalid `{@const (x = y)}` in Svelte files (2.5.14 regression)

Author: leekoolCreated Sep 18, 2026Updated Sep 18, 2026
LabelsS-Bug-confirmedS-Has workaroundL-HTML

summary

On a Svelte file, with html.experimentalFullSupportEnabled: true and html.formatter.enabled: true, biome check --write rewrites

svelte
{@const x = y}

into

svelte
{@const (x = y)}

which is not valid Svelte: Expected identifier or destructure pattern (https://svelte.dev/e/expected_pattern). Svelte 5.56.9 and 5.57.0 both reject it.

The rewrite is silent: no rule is named, no diagnostic is emitted, and biome check (without --write) reports nothing on a file that already contains the invalid form. One formatter run leaves the file unparseable, so the dev server serves an error overlay and the app never boots.

Regression: 2.5.13 and 2.4.0 do not do this.

environment

  • @biomejs/biome 2.5.14 (CLI, darwin-arm64, also reproduced with bunx @biomejs/[email protected])
  • macOS 15 (arm64), bun 1.x
  • Svelte 5.57.0 (also rejects it on 5.56.9)
  • regressed from 2.5.13 (2.5.13 and 2.4.0 both behave correctly)

config

Two keys are enough:

json
{
    "html": {
        "experimentalFullSupportEnabled": true,
        "formatter": { "enabled": true }
    }
}
  • html.experimentalFullSupportEnabled: true alone does not reproduce (biome does not re-print the template).
  • html.formatter.enabled: true without the experimental flag does not reproduce.
  • Both together: reproduces, deterministically, on every run.

reproduction

Probe.svelte:

svelte
<script>
    let messages = [{ role: "user", text: "a" }];
    let streamingMessageIndex = 0;
</script>

{#each messages as msg, i}
    {#if msg.role === "system"}
        <p>system</p>
    {:else if msg.role === "tool"}
        <p>tool</p>
    {:else}
        {@const streaming = i === streamingMessageIndex}
        {@const steps = msg.text}
        <p>{streaming} {steps}</p>
    {/if}
{/each}
bash
biome check --write --config-path=biome.json Probe.svelte

actual

svelte
    {:else}
        {@const (streaming = i === streamingMessageIndex)}
        {@const (steps = msg.text)}
        <p>{streaming} {steps}</p>
    {/if}

Checked 1 file. Fixed 1 file. — no rule name, no diagnostic, no unsafe-fix confirmation. The rewrite is silent.

expected

The file unchanged: it is already formatted, and {@const x = y} is the only syntax Svelte accepts here.

impact

The rewritten form does not parse:

{svelte compile} Expected identifier or destructure pattern
https://svelte.dev/e/expected_pattern

So one biome check --write leaves the file syntactically invalid: the dev server serves an error overlay, the app never boots, and any screenshot/e2e run against that checkout fails. It also survives review easily, because biome reports nothing.

Both sides of it are silent:

  • on write: Fixed 1 file, no rule attributed, so a formatter run looks clean;
  • on check: biome check Probe.svelte (no --write) reports nothing on a file that already contains the invalid {@const (x = y)} — biome accepts what biome produced.

notes from narrowing it down

run result
check --write, 2.5.14, flag + html.formatter.enabled parens added (2/2 runs)
check --write, 2.5.14, flag only unchanged
check --write, 2.5.14, html.formatter.enabled only unchanged
format --write, 2.5.14, same config unchanged (also unchanged on deliberately mis-indented input)
lint --write, 2.5.14, same config unchanged
check --write, 2.5.13 / 2.4.0, same config unchanged
check --write with --formatter-enabled=false --linter-enabled=false --assist-enabled=false still added parens
check --write with html.experimentalFullSupportEnabled: false unchanged
check --write with html.formatter.enabled: false unchanged

Two things worth flagging for whoever picks this up:

  1. format --write and check --write disagree on the same file and config. check re-prints the template and mangles {@const}; format re-prints it and does not. The three --*-enabled=false flags being ignored suggests the rewrite is not a normal fix pass.
  2. The trigger needs html.formatter.enabled, which is why a html.experimentalFullSupportEnabled-only config looks innocent.

Workaround in the affected project: pin @biomejs/biome to 2.5.13, which formats Svelte files correctly and does not rewrite {@const}.