Minimal reproduction for a bug in @biomejs/biome 2.4.10 where the assist/source/useSortedAttributes action duplicates one JSX attribute and silently drops another when both an outer JSX element and one of its nested JSX-valued attributes have unsorted attributes.
The duplicated output is then immediately flagged as lint/suspicious/noDuplicateJsxProps by the same Biome run, so the auto-fix produces source that fails Biome's own linter.
The full upstream issue draft (ready to paste into https://github.com/biomejs/biome/issues/new) lives at UPSTREAM_ISSUE.md.
@biomejs/biome2.4.10ultracite7.4.3 (extendsultracite/biome/core+ultracite/biome/react)- React 19, Vite 8, TypeScript 6 — none of these matter for the bug; Biome operates on source text.
The fastest path is the bundled script — it installs deps on first run, restores the pristine input, runs the auto-fix, asserts that noDuplicateJsxProps was emitted, prints the diff, and restores the pristine state for the next run:
./reproduce.shExit codes: 0 = bug reproduced, 1 = bug missing (Biome may have fixed it — investigate), 2 = setup error.
Manual equivalent:
bun install
cp src/Repro.pristine.tsx src/Repro.tsx
bun x biome check --write --only=assist/source/useSortedAttributes src/Repro.tsx
bun x biome check src/Repro.tsx
# → src/Repro.tsx:5:17 lint/suspicious/noDuplicateJsxPropssrc/Repro.pristine.tsx:
declare const Outer: (p: { z: string; a: unknown }) => unknown;
declare const Inner: (p: { z: number; a: number }) => unknown;
export function Repro() {
return (
<Outer
z="x"
a={
<Inner
z={1}
a={2}
/>
}
/>
);
}After bun x biome check --write --only=assist/source/useSortedAttributes src/Repro.tsx:
declare const Outer: (p: { z: string; a: unknown }) => unknown;
declare const Inner: (p: { z: number; a: number }) => unknown;
export function Repro() {
return <Outer a={<Inner a={2} z={1} />} a={<Inner a={2} z={1} />} />;
}The <Outer> element ends up with a duplicated and z dropped entirely.
Bisected against the minimal repro:
| Variant | Outer attrs | Inner attrs | Outer needs sort | Inner needs sort | Bug fires? |
|---|---|---|---|---|---|
| Both unsorted (the repro) | z, a |
z, a |
✅ | ✅ | ✅ |
| Outer sorted, inner unsorted | a, z |
z, a |
❌ | ✅ | ❌ |
| Outer unsorted, inner sorted | z, a |
a, z |
✅ | ❌ | ❌ |
| Both sorted | a, z |
a, z |
❌ | ❌ | ❌ |
| Outer unsorted, value is a string/number/identifier | z, a |
n/a | ✅ | n/a | ❌ |
The bug requires the assist to produce a code-fix on both the outer and a nested JSX element in the same fix pass. With anything else — single side, sorted side, no nested JSX — the fix runs cleanly.
| Command | Formatter | Linter | Assist | Bug fires? |
|---|---|---|---|---|
biome format --write src/Repro.tsx |
✅ | ❌ | ❌ | ❌ |
biome lint --write src/Repro.tsx |
❌ | ✅ | ❌ | ❌ |
biome check --write src/Repro.tsx |
✅ | ✅ | ✅ | ✅ |
biome check --write --only=assist/source/useSortedAttributes src/Repro.tsx |
(off) | (off) | ✅ (only this action) | ✅ |
bun x ultracite fix src/Repro.tsx |
✅ | ✅ | ✅ | ✅ (calls biome check --write internally) |
biome format and biome lint do not run assist actions (source) — only check and ci do. The --only=assist/source/useSortedAttributes form is the cleanest minimal command for an upstream issue: it shows the bug fires even when every other rule and action is disabled.
use_sorted_attributes.rs on main constructs the code-fix by zipping originals with sorted versions and calling replace_node_discard_trivia in positional pairs. When two useSortedAttributes mutations target nested elements (outer + nested) in the same pass, the byte-range tracking on the inner subtree appears to confuse one of the outer's slot replacements: the deletion silently no-ops, leaving the original in place, while the sibling slot's replacement is lost. See UPSTREAM_ISSUE.md for the full hypothesis.
Disable the assist in biome.jsonc:
This is surgical — formatter, linter, and every other assist action keep working.
No public issue matches this exact failure mode (searched 2026-04-09). Adjacent bugs in the "Biome auto-fix corrupts JSX" family:
- biomejs/biome#9296 / #9298 —
useSortedAttributescomments not moving with attributes (different failure mode, fixed in 2.4.6, already in 2.4.10). - biomejs/biome#4553, #4871 —
noUselessFragmentsproduces invalid JSX. - biomejs/biome#8011 —
useConsistentCurlyBracessuggests invalid JSX.
src/Repro.pristine.tsx— frozen 16-line minimal trigger. Restore oversrc/Repro.tsxbefore each run.src/Repro.tsx— working copy.reproduce.sh— pass/fail oracle.UPSTREAM_ISSUE.md— ready-to-paste GitHub issue draft.biome.jsonc— extends ultracite presets;useSortedAttributesis in its default-enabled state.package.json— pins@biomejs/biome 2.4.10andultracite 7.4.3.
{ "assist": { "actions": { "source": { "useSortedAttributes": "off" } } } }