Diagnosing Biome Result Drift Between Local and CI Environments
When `biome check` or formatting results differ between local machines and CI, the cause is usually version drift, config resolution, ignore semantics, or daemon caching. This guide provides ordered checks and fixes.
10 Feb 2026, 15:32 UTC

Recognizable condition
The same commit formats cleanly on a laptop but fails in CI. A suppression comment that works locally is ignored on the build server. Or a file that should be linted passes silently because it was never processed. When Biome produces different results across environments while the committed configuration looks identical, the divergence usually traces to four variables: the exact installed version, the nearest resolved configuration file, the set of files Biome actually traverses, and whether a background daemon is serving cached results. This guide isolates those variables in order.
Cause and diagnostic table
| Symptom | Likely cause | First diagnostic |
|---|---|---|
| Error only in CI | Version drift; formatter defaults or rule sets changed between releases | Run biome --version in both environments |
| File not linted or formatted | Ignore semantics or size limit; files.maxSize silently skips large files | Run biome check --verbose and inspect the traversed file list |
| Suppression ignored | Rule name mismatch or syntax error in // biome-ignore | Compare the exact rule name in the output with the comment |
| Format differs from Prettier-era habits | indentStyle, lineWidth, lineEnding, or quoteStyle defaults | Read those keys from the resolved config |
| Stale results after a config edit | Daemon cache serving old state | Re-run with the daemon disabled |
Ordered checks
- Pin and compare versions. Run in the project root:
biome --version. Compare the output with the version declared inpackage.jsonand the lockfile. Risk: a globally installed Biome can shadow the project-local binary, so prefer invokingnpx biomeor./node_modules/.bin/biome. - Inspect loaded configs and traversed files. Run
biome check --diagnostic-level=debug --verbose. Expected check: the debug output should list everybiome.jsoncfile loaded and every file traversed or ignored. Risk: verbose output can be large in monorepos; pipe it to a file if needed. - Force a single config. Run
biome check --config-path ./biome.jsonc, replacing./biome.jsoncwith the intended root config. If the divergence disappears, a nestedbiome.jsoncor anextendschain is overriding settings. - Isolate VCS ignore effects. Temporarily set
vcs.useIgnoreFiletofalsein the nearest config and re-run. If the file set changes,.gitignoreis influencing Biome. CI containers may lack a usable VCS client or use shallow copies, making this behavior unreliable. - Disable the daemon. Re-run the same command with the daemon disabled. Because daemon activation differs across Biome versions, verify the exact mechanism against
biome check --helpfor your installed release; in some versions this means omitting--use-server, in others it means stopping the background process. - Isolate the failing concern. Run
biome check --formatter-enabled=falseto see only lint and assist results, thenbiome check --linter-enabled=falseto see only formatting. If your version predates the rename, substitute--organize-imports-enabledfor--assist-enabled. A reported "formatting" failure may originate from an assist action or a lint rule, not the formatter itself.
Fixes tied to findings
Version drift
Pin an exact Biome version in package.json under devDependencies and commit the lockfile. Invoke the local binary rather than a global install so CI matches development. After changing versions, restart the daemon if one is active.
Config resolution and ignore semantics
Prefer explicit file sets over ambient .gitignore. The following example disables VCS-based ignoring and declares ignores directly, which produces deterministic behavior in containers without a VCS client:
{
"vcs": {
"enabled": true,
"useIgnoreFile": false
},
"files": {
"ignore": ["dist/**", "coverage/**", "*.min.js"]
}
}
Also check files.maxSize. Its default is roughly 1 MiB; files above that threshold are skipped silently, so a large file can appear to pass simply because it was never processed. Raise the limit only when necessary, because doing so can slow CI.
Formatter and suppression alignment
Formatter defaults differ from Prettier-era habits: Biome defaults to tabs for indentStyle and may differ on lineWidth, lineEnding, and quoteStyle. Declare these explicitly in the root config and run biome check --write once to normalize the tree. For suppressions, the comment must match the exact rule name, for example // biome-ignore lint/suspicious/noExplicitAny: legacy adapter. File-wide suppression uses // biome-ignore-all.
Daemon caching
After a version upgrade or config edit, restart the daemon or bypass it for that run. If daemon and non-daemon results disagree, treat that as a distinct failure mode rather than a formatting issue.
Escalation criteria
Open an upstream issue when all of the following hold:
- Both environments use an identical pinned version.
- A single explicit
--config-pathis supplied, ruling out nested orextendssurprises. - A documented default behaves contrary to the installed schema.
- A syntactically valid
biome-ignorecomment fails to suppress. - Daemon and non-daemon runs disagree on the same file set.
Include the exact version, a minimal repository reproduction, the resolved configuration, and the precise command used.
Limitations and practical verification
Biome's CLI flags, defaults, and suppression syntax are version-sensitive. Flags such as --organize-imports-enabled have been renamed to --assist-enabled in newer releases, and daemon behavior may be opt-in or default depending on the major version. Verify every command against the installed version's help text and schema rather than assuming documentation defaults. You can read defaults from node_modules/@biomejs/biome/configuration_schema.json in your project.
To confirm a fix, create a scratch repository with one deliberately misformatted file. Run npx @biomejs/biome@<exact-version> check locally and in a clean container; the outputs should be byte-identical. Use --diagnostic-level=debug --verbose to confirm which configs load and which files are traversed. Toggle vcs.useIgnoreFile to confirm whether .gitignore changes the processed set. Finally, run the same command with and without the daemon after a config edit to ensure no stale results are served.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.