Codex writes a clean-looking patch. You open the file in your editor and Prettier-on-save rewrites every line: quotes flip, trailing commas appear, semicolons get added or stripped, line breaks rearrange. Now the git diff is twice as long as the actual change. Worse, when the agent tries to follow up on its own edit later, it cannot match its original lines anymore. Pre-commit hooks fail. CI lint flags the file. The “ten lines changed” turns into 300.
Fastest fix: add one rule to your AGENTS.md so Codex formats with the project’s pinned Prettier before it ever reports “done”:
Run `pnpm exec prettier --write` on every file you touch.
Then `pnpm exec prettier --check` must return 0 before you finish.
That single handshake removes the editor as a hidden second formatter. The rest of this page covers why your draft still drifts (version mismatch, AGENTS.md vs .prettierrc disagreement, ESLint fighting Prettier) and how to make the agent’s first draft already match what Prettier will produce.
This is not a Codex bug; it is a missing handshake. The agent does not know your .prettierrc exists, or it knows but the prompt did not load it, or it ran format but with default settings instead of project settings. The fix is to make Prettier part of the agent’s “done” definition, and to feed the relevant rules into the agent’s style hints so its first draft already matches what Prettier will produce.
Which bucket are you in
| Symptom | Most likely cause | Jump to |
|---|---|---|
Diff is 5x bigger than the agent reported; prettier --check fails | Agent never formatted; editor did on save | Cause 1, Step 1 |
Uniform changes (all ' to ", semicolons everywhere) | Agent ran a different Prettier version | Cause 2, Step 3 |
| Agent’s draft is consistently “wrong” before any save | AGENTS.md style disagrees with .prettierrc | Cause 3, Step 2 |
| File flips back and forth depending on which tool runs last | ESLint and Prettier both formatting | Cause 4, Step 4 |
| Stray non-ASCII (smart quotes, nbsp) only in agent-written lines | Agent emitted invisible characters | Cause 5, Step 5 |
| A hand-aligned block keeps getting nudged | Edit landed inside a // prettier-ignore zone | Cause 6, Step 6 |
Common causes
Ordered by hit rate.
1. Agent never ran Prettier; editor did
The agent finished, saved the file, declared “done”. On your machine, editor-on-save Prettier reformatted the file. The clean-looking output gets transformed at the last second.
How to spot it: git diff looks much larger than the agent reported. Run prettier --check <file> — many lines fail.
2. Agent ran a different Prettier than the project’s
pnpm prettier --write vs npx prettier@3.6 --write vs a globally-installed prettier produce different output. Prettier ships breaking-ish formatting tweaks across minor versions (3.5 added the objectWrap and experimentalOperatorPosition options, 3.6 shipped an experimental fast CLI, and the line stands at 3.8.x as of June 2026), so a global prettier that resolves to 3.8 will not match a project pinned to 3.3.
How to spot it: pnpm exec prettier --version inside the project differs from whatever the agent ran. The diff is uniform formatting noise (for example every ' becomes ", or trailing commas appear on every multiline call).
3. Style hints in AGENTS.md disagree with .prettierrc
AGENTS.md says “use single quotes” but .prettierrc has "singleQuote": false. Agent obeys AGENTS.md; Prettier wins on save. They are fighting.
How to spot it: Manually diff AGENTS.md style claims against .prettierrc / prettier.config.js. Any disagreement = noisy diffs forever.
4. Mixed Prettier and ESLint formatters
ESLint with eslint-plugin-prettier AND a standalone Prettier on save produce slightly different orderings of arrow parens, trailing commas in JSX. Agent picked one, the chain of tools picked the other.
How to spot it: Files reformat differently depending on which tool you run last. Run pnpm prettier --check . && pnpm eslint . — if both pass after prettier --write but eslint --fix then changes lines back, you have a conflict.
5. Agent inserted invisible characters Prettier reformats
Smart quotes, non-breaking spaces, en-dashes — Codex sometimes inserts these in comments or strings. Prettier (or some plugin) normalizes them.
How to spot it: cat -A <file> | grep -E '\xc2\xa0|\xe2\x80' finds non-ASCII. Compare to the agent’s output text.
6. Files with // prettier-ignore directives
Existing code uses // prettier-ignore to preserve a hand-aligned block. Agent edits inside the block — Prettier respects the ignore, but the agent’s careful alignment may not match the existing alignment.
How to spot it: grep -rn "prettier-ignore" <files> lists the protected zones. Cross-check with agent’s edited regions.
Before you start
- Save a snapshot:
git stashorgit diff > before.patchso you can compare cleanly. - Note your Prettier version:
cat package.json | grep '"prettier"'andpnpm exec prettier --version(use the project-local resolver, not global). - Note your editor’s Prettier integration: in VS Code,
editor.formatOnSave,editor.defaultFormatter(esbenp.prettier-vscode), andprettier.requireConfig. Heads-up: since the Prettier VS Code extension v12.1.0 (Jan 2026) the extension no longer auto-checks thateditor.defaultFormatteris set to Prettier andeditor.formatOnSaveistrue, so confirm both explicitly instead of assuming they are wired up.
Information to collect
.prettierrc/prettier.config.js/package.json#prettiercontents.- AGENTS.md sections about style.
- The exact files reformatted on save (compare
git difflines vs what the agent wrote). - Editor of choice + extensions (VSCode + Prettier extension, etc.).
- Whether
prettier-eslintoreslint-config-prettieris installed.
Step-by-step fix
Ordered by ROI.
Step 1: Make Prettier part of the agent’s “done” loop
Put this in AGENTS.md (Codex reads it automatically at session start) or in the task prompt:
After every file edit, run:
pnpm exec prettier --write <files-edited>
Confirm: pnpm exec prettier --check <files-edited> returns 0.
If non-zero, fix and re-run before declaring done.
AGENTS.md is the durable home for this, per OpenAI’s Codex guidance — it loads into context on every run, so you do not have to repeat the rule in each prompt. Now the agent’s last action matches what your editor will produce: zero post-save reformat. On large repos, prettier --cache (cache is on by default in Prettier 3.6+) keeps the per-edit check fast.
Step 2: Align AGENTS.md style claims to .prettierrc
Open .prettierrc (or prettier.config.js) and translate every option into AGENTS.md:
## Formatting (matches .prettierrc — do not deviate)
- printWidth: 100
- semi: false → no trailing semicolons
- singleQuote: true → use single quotes
- trailingComma: "all"
- arrowParens: "always" → (x) => x not x => x
- bracketSpacing: true → { foo } not {foo}
Codex’s first draft now matches Prettier’s output — eliminating the diff inflation.
Step 3: Pin the Prettier version the agent calls
Force the project’s pinned version:
After every edit:
pnpm exec prettier --write <files>
# NOT: npx prettier --write (may download a newer Prettier, e.g. 3.8 vs your pinned 3.3)
# NOT: prettier --write (may resolve to a global install)
pnpm exec (or yarn exec / npm exec) routes to the version in your lockfile. Pin the exact version in package.json (for example "prettier": "3.8.4", not "^3.0.0") so the agent, your editor, CI, and your teammates all run byte-for-byte the same formatter.
Step 4: Resolve ESLint + Prettier conflicts permanently
The fix is eslint-config-prettier, which turns off every ESLint rule that overlaps with Prettier so the two tools stop fighting:
pnpm add -D eslint-config-prettier
If you are on ESLint 9 flat config (eslint.config.js, the default since ESLint 9), import the /flat entry and put it last in the array so it wins:
// eslint.config.js
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import eslintConfigPrettier from 'eslint-config-prettier/flat'
export default [
js.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier, // must be last
]
On the legacy .eslintrc format, add 'prettier' last in extends:
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier']
Do not use eslint-plugin-prettier to run Prettier as an ESLint rule. As of 2026 the Prettier maintainers discourage it: it makes ESLint slower, turns formatting differences into red squiggly “errors”, and gives the agent two formatters to satisfy. Keep the split clean — ESLint for correctness, Prettier for formatting. After this, eslint --fix and prettier --write produce identical output and stop reverting each other.
Step 5: Normalize invisible chars in agent output
Add to the task prompt:
Output rules:
- ASCII only in code (no smart quotes, no en-dashes, no nbsp)
- For comments: standard ASCII " ' - --
- For string literals: only what the user requested literally
For paranoia, run a sanitizer after edits:
sed -i 's/\xc2\xa0/ /g; s/\xe2\x80\x9c/"/g; s/\xe2\x80\x9d/"/g' <files>
Step 6: Handle prettier-ignore zones explicitly
In the task prompt, list protected zones:
The following blocks are `// prettier-ignore` protected — preserve their existing alignment exactly. Do not edit unless required:
- src/data/timezones.ts:42-78
- src/config/menus.tsx:120-180
If you must edit inside one, match the existing indent / alignment byte-for-byte.
Step 7: Optional — disable editor format-on-save during agent runs
If you cannot get the agent’s Prettier to match editor’s, temporarily:
"editor.formatOnSave": false
Then run Prettier yourself at the end of the session. Removes the editor as a hidden actor.
Verify
git diffon the agent’s output: line count should match what the agent reported, not 5x.pnpm prettier --check <files>returns 0 immediately after agent finishes.- Open the file in your editor, save without typing anything: zero changes.
- Pre-commit hook (
lint-staged+prettier --check) passes on first try.
Long-term prevention
- AGENTS.md formatting section is auto-generated from
.prettierrcso they cannot drift. - Every agent task template includes “run prettier on touched files before done”.
- Use
pnpm exec(or equivalent) in agent tool calls — never globalprettier. - Install
eslint-config-prettierso ESLint never argues with Prettier. - Add a CI job that runs
prettier --checkon PR — it catches drift before merge. - For ASCII purity, add a pre-commit hook that rejects non-ASCII in
.ts/.jsfiles.
Common pitfalls
- Running
prettier --writeon the whole project after an agent edit — masks which lines the agent actually touched and bloats the diff. - Editing AGENTS.md style section but forgetting to update
.prettierrc(or vice versa) — they will fight again next month. - Trusting
npx prettierwithoutpnpm exec— version pinning is silently ignored. - Leaving
editor.formatOnSaveon while you “just check” the agent’s diff — the moment you click into the file, it gets reformatted. - Ignoring “trailing comma” disagreements as cosmetic — they break
git blameand inflate every future diff.
FAQ
Q: I added Prettier to the agent loop but the editor still reformats on save. Why?
Two usual suspects. First, prettier.requireConfig: if it is false, the editor formats with Prettier defaults and ignores your .prettierrc. Set it true so the editor only runs Prettier when it finds a config. Second, since the Prettier VS Code extension v12.1.0 (Jan 2026) the extension stopped auto-confirming that it is the active formatter, so explicitly set "editor.defaultFormatter": "esbenp.prettier-vscode" and "editor.formatOnSave": true (scope per-language with "[typescript]" blocks if a different formatter is winning).
Q: We migrated to ESLint 9 flat config and eslint-config-prettier stopped working. What changed?
Flat config uses a different import. Use import eslintConfigPrettier from 'eslint-config-prettier/flat' and place it last in your export default [ ... ] array. The bare eslint-config-prettier (no /flat) and the extends: ['prettier'] string both belong to the old .eslintrc format and silently do nothing in flat config.
Q: My team uses Biome / dprint, not Prettier. Same fix?
Yes — substitute biome format --write or dprint fmt for the Prettier commands. The structural fix (formatter inside the agent loop + style hints aligned to config) is identical.
Q: Can I just tell Codex to not format at all?
You can, but then you owe it a clear “we will reformat outside the agent” workflow. The diffs will still be noisy on commit. Better to align the agent’s first draft to what Prettier will produce.
Q: What about .prettierignore?
Same story — make sure AGENTS.md lists patterns the agent should not touch, matching .prettierignore. Otherwise the agent edits a generated file that Prettier refuses to format and you get inconsistency.
Related
- Codex misses project conventions
- Codex generated code does not fit existing style
- Codex Agent stops mid-task without error
- Codex Agent spawns too many redundant tool calls
- Codex patch conflicts with existing code
- Codex environment setup fails
- Codex Agent Skips Files Containing Binary Data
- Codex Cannot Access a Private Repo
- Codex PR Description Says “Refactored Components” and Nothing Else
- Codex Stalls on a Merge Conflict or Resolves It the Wrong Way
- Codex Added a Package but the Lockfile Did Not Change
- Codex Creates a New TypeScript Interface Duplicating One That Already Exists
- Codex Says Tests Passed but Actually Skipped the Failures
- Codex Bails Out When the Patch Gets Too Large
External references
- eslint-config-prettier — flat config setup (the
/flatimport + ordering) - Codex: custom instructions with AGENTS.md (where to put the format-on-done rule)
- Prettier configuration options (the option names to mirror in AGENTS.md)