Why a native yarn audit fix is a hard problem, the tools and services that help in the meantime, and how we try to lend the Yarn ecosystem a hand — with remediation rebuilt on a lockfile graph.
-
yarn audit[still does not]fix yarn.lock: you can'tseda graphyarn.lock: may the--forcebe with you
TL;DR
-
yarn audit(andyarn npm auditin Berry) reports vulnerabilities; turning that into an automatic fix has never been part of Yarn. npm hasnpm audit fix, pnpm haspnpm audit --fix— Yarn, Classic through 4, doesn't. - Not for lack of asking (the request has been open since 2019) — it's a hard problem on a finite maintenance budget, and "fixing" a lockfile is re-resolution, not search-and-replace.
- SaaS bots (Dependabot, Renovate, Snyk, Socket) solve a different shape — asynchronous, hosted, PR-gated — and leave a gap for local, one-command remediation.
- We've tried to fill that gap, in the open:
yarn-audit-fixdoes remediation as a deterministic lockfile-graph transformation — registry-only, nonpm/yarnsubprocess — emitting a lockfile that still passesyarn install --immutable.
audit ≠ audit fix
Years ago I ran into something every Yarn user eventually does: yarn audit happily prints a wall of CVEs, and then you're on your own for the fix. I wrote up the first workaround back then — Yarn audit fix: workaround — which did something slightly horrifying but effective: convert yarn.lock → package-lock.json (via Synp), run npm audit fix --package-lock-only, convert back. A year later, when Yarn 2 (Berry) changed everything, I followed up with The missing yarn audit fix for Yarn 2+ Berry, which dropped the conversion dance and patched the lockfile directly from yarn npm audit --json.
Those posts are old. The problem they describe is not.
The naming confuses people, so let's be precise:
-
npm audit fix— bumps vulnerable packages to patched versions within your declared ranges, in place, locally, in one command. With--forceit will also do semver-major jumps (more on why that's risky below). -
pnpm audit --fix— pnpm took the overrides route: it writes pinned non-vulnerable versions into overrides (and, since v11,--fix=updatecan update the lockfile instead). See the pnpm docs. -
yarn audit fix— isn't a command. There's no built-in verb that takes the audit report and applies it: Classic'syarn auditand Berry'syarn npm auditfilter and format the report (--level,--severity,--exclude,--ignore,--json) but never touch the lockfile.
For a single app you can shrug and run npm in a temp dir. For a Yarn-only monorepo — workspaces, constraints, PnP, a yarn.lock that's the single source of truth — that escape hatch closes.
Years of asking — for something hard
This isn't an obscure request; the paper trail is long:
-
2019 —
yarn#7075[feat] yarn audit fix, on Classic. -
2021 —
berry#3582[Feature]yarn npm audit --fix, whose author volunteered to implement it. -
2025 —
berry#6959, reframing the gap as a leaky abstraction; closed as a duplicate of the open request.
A community member even built a plugin — sargunv/yarn-plugin-npm-audit-fix — and offered to upstream it. It hasn't landed in core, and the plugin's repository has since been archived. Keeping it out of core is fair: a remediation engine inside a package manager competes with the installer, PnP, constraints, and the plugin API for a small team's time — a higher bar than a standalone tool.
The request has outlived Classic, 2, 3 and 4 and every lockfile schema between them — exactly the gap a community stopgap fills.
Where the difficulty comes from
It's tempting to read years of an open issue as a quiet no. It isn't: the feature is plainly wanted — it just hasn't been built yet, because building it well is hard. Architecture, finite maintainer bandwidth, awkward data; any one would slow a feature down, and together they explain the timeline.
1. Audit is a thin proxy, by design. yarn npm audit --json is largely a pass-through to the registry's audit endpoint. Yarn returns the server's verdict; it doesn't own a remediation model. That's the "leaky abstraction" from #6959: the report reflects the registry's worldview, which isn't the same as Yarn's resolution graph. Reasonably, Yarn's maintainers have scoped remediation to yarn up --recursive and resolutions rather than the audit command — leaving auto-fix as plugin territory.
2. The advisory data is awkward to build on. Advisory ranges aren't always valid semver (>1.0.0 <2.0.0 and friends need coercion before you can compare). And the signal itself is still settling: audit IDs aren't stable across runs, ignores used to misfire for a package included multiple times, and the endpoint has returned 400s on ordinary lockfiles. Reliable auto-fix needs a stable input, and much of that input lives upstream of Yarn.
3. "Fixing" a lockfile is re-resolution, not string replacement. Bump one entry and its transitive dependencies should change too — but, as I found writing the Berry tool, Yarn doesn't reload a patched entry's dependencies for you. A correct fix has to re-resolve subtrees against the registry and keep the lockfile internally consistent and deterministic. With PnP and Yarn's strict linker, a naive in-place edit yields an install that doesn't reproduce. The community plugin takes the pragmatic route: ask Yarn's own resolver for the highest version within a package's declared range, and if it clears the advisory, pin that version and re-install. That works right up to the ceiling where most deep transitives live — when the only patched version is a cross-major bump out of range, the highest in-range candidate still doesn't clear the advisory, so it reports no compatible patched version and gives up on that package. Its own roadmap names the rest of the climb: walk up the tree to fix via parents, update manifests for direct deps, add a flag for out-of-range bumps. None of it is trivial.
4. Most vulnerabilities are transitive and deep. The vulnerable package is rarely one you installed. When it's three levels down, the parent controls its version, and the only real fix is often a cross-major bump that breaks the consumer's declared range. Doing that safely — and knowing when not to — is the actual hard part.
5. npm's audit fix shows the trap. Even copying npm wouldn't be a clean win. In range, npm audit fix often reports "fixed 0 of N" because the patched version is out of range; reach for --force and it can silently do semver-major upgrades and break builds; and a declared overrides entry could block it from replacing even eligible dependencies until npm 11.2. A built-in yarn audit fix would inherit all of this — plus PnP.
The remediation landscape (and the gap for a local fix)
So how do teams remediate on Yarn? Two tiers, and the gap they leave.
Built-in / local (manual)
yarn up <pkg> --recursive per advisory, or pin transitive versions via resolutions, then re-install. This is the maintainer-endorsed path and it works — but it's manual, per-package, and easy to get subtly wrong at scale.
The bots (asynchronous, hosted)
- Dependabot — GitHub-native, zero-config, opens PRs from the GitHub Advisory DB. No reachability analysis; remediation is "here's a PR, you review and merge."
- Renovate — the most configurable updater, multi-platform. It's an update tool, not a scanner — you layer SCA on top.
- Snyk — its own advisory DB (often ahead of NVD), automated fix PRs, monitoring, IDE plugins. Commercial, cloud.
- Socket.dev — a different axis: behavioral / supply-chain analysis (malicious install scripts, network access, typosquats) that CVE scanners miss. Detection-first.
These are excellent, and most serious teams should run one. But they share a shape: asynchronous, hosted, repo-bound, review-gated. They assume a cloud-connected VCS, a bot identity, and a PR cadence.
The gap they leave
What none of them gives a Yarn user is the thing an npm user gets from npm audit fix: a local, synchronous, one-command, scriptable "fix my yarn.lock now." That shape matters exactly where many enterprises live:
- air-gapped / regulated environments where SaaS bots and a chatty GitHub App are non-starters;
- a CI gate that must remediate in the same job, not three PRs later;
- pre-commit / local dev loops where you want the fix before you push;
-
Yarn monorepos, where bot support for workspaces + PnP +
resolutionsis uneven.
That gap is why community tools exist at all — yarn-audit-fix, the sargunv plugin, and other takes on it. The need is real and the core feature is hard, so the ecosystem fills in around it. That's open source working as intended.
How we try to help
We've kept at it, in the open. yarn-audit-fix tracks every Yarn lockfile schema that shipped — Classic (v1), Berry v4–v6 (Yarn 2/3), and Yarn 4+ (Berry v8/v9/v10) — detecting the version at runtime and parsing each into one shared dependency graph.
Two things have shifted since those early posts.
Where the data comes from. The first versions shelled out: convert the lockfile, run someone else's audit fix, parse the CLI output back. The current line talks to the registry directly — it reads advisories from the registry's bulk audit endpoint, resolves fix versions (and the dependencies they pull in) from package metadata, and fetches tarballs to recompute integrity hashes. No npm or yarn subprocess anywhere — no second package manager to install, no CLI output format to keep chasing, just HTTP to a registry you already trust, using your existing scoped .npmrc / .yarnrc.yml auth (host-bound, https-only).
How the fix is computed. Remediation is a deterministic transformation over that graph (lockgraph): move the vulnerable node to the lowest published version that clears the advisory, rebind the edges that pointed at it — and then re-resolve that new version's own dependency closure from the registry. That last step is the one flagged as the hard part above: a patched package's transitive deps can differ from the version you had — simple-git past 3.34, for instance, splits out @simple-git/args-pathspec and @simple-git/argv-parser — and Yarn won't reload them for you. Pull them in and the result is a lockfile that actually reproduces: the resolutions are the ones a clean yarn install would compute, and the lock passes yarn install --immutable. A compatibility gate keeps the default safe — a fix applies only when it still satisfies every surviving consumer's declared range, with the riskier cross-major bumps behind an explicit --force.
Building on a shared model has a nice side effect: when the real-world corpus turns up a parser gap, we file it and it gets fixed — the ecosystem gets a little better for everyone, not just for us.
This is meant as a contribution, not a verdict. If Yarn ships a native audit fix one day, we'll happily retire ours and help however we can. Until then, consider this a community on-ramp.
The graph model itself — the edge-redirect patch, the closure re-resolution, the compatibility gate, and how it stays faithful to every Yarn lockfile format from Classic to Berry v10 — is the next part.
Sources
- Yarn issues:
yarn#7075,berry#3582,berry#6959,berry#4117,berry#6438,berry#5104 - Community plugin:
sargunv/yarn-plugin-npm-audit-fix - npm / pnpm:
npm/cli#7987, pnpm audit docs,pnpm#8943, Whynpm audit fix --forceis risky - Tooling comparisons: SCA tools compared, Dependabot vs Renovate vs Snyk
- Yarn versions / EOL: endoflife.date/yarn
- Earlier notes: Yarn audit fix: workaround, The missing
yarn audit fixfor Yarn 2+ Berry
Top comments (0)