Yarn: install fails with an integrity checksum mismatch
Hi, it's BlueByte. When yarn install stops with Integrity check failed — or, on modern Yarn, error code YN0018 — nothing is wrong with your code. Yarn compared the tarball it has (downloaded or cached) against the checksum recorded in your yarn.lock, and they don't match, so it refuses to link a package it can't vouch for. We'll walk through what the message means, how to tell which Yarn you're on (the fix differs), why the checksum drifts, how to confirm the cause, fix it either way, and keep it from coming back.
What "Integrity check failed" (and YN0018) is telling you
On Yarn Classic (v1) the failure names the package and the hash it computed:
error Integrity check failed for "left-pad" (computed integrity doesn't match our records, got "sha512-...")On modern Yarn (Berry, v2+) the same failure carries an error code. Yarn's error index calls YN0018 CACHE_CHECKSUM_MISMATCH and defines it as "The checksum of a package from the cache doesn't match what the lockfile expects." Both forms mean the same thing — the hash Yarn computed for the bytes it has does not equal the hash the lockfile says it should be. Yarn is doing its job: it won't silently install a package whose bytes changed under it.
First, check which Yarn you're actually running
The fix commands and config keys diverge between the two lines, so confirm your version before you touch anything:
yarn --version1.x is Yarn Classic; 2.x, 3.x, or 4.x is modern Yarn (Berry). A repo can pin its own version through packageManager in package.json or yarnPath in .yarnrc.yml, so the Yarn on your PATH isn't always the one the project uses — run the command from inside the project directory.
Why the checksum stops matching
The hash drifts for a handful of concrete reasons:
- The local cache is corrupt — a partial download, an interrupted install, or a disk issue left a truncated or altered tarball.
- The lockfile was built against a different registry — a private mirror or proxy repackaged the tarball, so its bytes (and hash) differ from the one your lock recorded.
- Someone edited a cache archive by hand while debugging — Yarn's docs call this out as the most common trigger for YN0018.
- You switched Yarn versions or hash algorithms, so an old lock carries checksums the new resolver computes differently.
- Rarely, a registry re-published the same version with different bytes — it shouldn't happen, but private registries sometimes do.
None of these is a bug in your dependency; the mismatch is between the tarball on disk and the lock's record of it.
Read the message: a corrupt cache vs. a changed package
Where the mismatch shows up tells you where to look. If it's a single package and only on your machine, suspect a corrupt local cache. If your whole team or CI hits the same package, suspect the registry or the lockfile. Check what's actually cached:
yarn cache dir # Classic: prints the global cache path
ls .yarn/cache # Berry: per-project cache of .zip archivesIf the mismatch survives a fresh clone on a colleague's machine, the lockfile itself carries the wrong checksum and needs regenerating — not just a cache wipe.
Fix it on modern Yarn (Berry)
Berry's cleanest fix is to purge the offending entry and refetch. The checksumBehavior setting decides what happens on a mismatch — throw (the default), reset, update, or ignore. Use reset for one run to purge and re-download:
YARN_CHECKSUM_BEHAVIOR=reset yarn installPer the docs, reset means "the cache entry will be purged and fetched anew." If you've verified the new bytes are legitimate — say a trusted mirror — refetch and re-verify every checksum instead:
yarn install --check-cacheThe docs describe --check-cache as "always refetch the packages and ensure that their checksums are consistent." As a last resort, update writes the computed hash back into yarn.lock — only do that when you trust the source of the new bytes.
Fix it on Yarn Classic (v1)
Classic has no checksumBehavior; you clear the cache and reinstall. Remove the stale node_modules and its integrity marker, clean the cache, then install fresh:
yarn cache clean
rm -rf node_modules
yarn installIf the lock's recorded hash is the wrong one — for example after moving registries — regenerate it:
yarn install --update-checksumsThat "update checksums in the yarn.lock lockfile if there's a mismatch between them and their package's checksum." Commit the corrected lockfile so everyone gets the fixed value.
A real case: a half-downloaded package in CI
A CI job is evicted mid-yarn install. The next build restores the cache from the previous run and fails with YN0018 on react-dom@npm:18.3.1. Only this one package, only in CI — a classic corrupt-cache signature. You change that step to YARN_CHECKSUM_BEHAVIOR=reset yarn install, the runner purges the truncated .zip, refetches it, the hash matches, and the build goes green. Nothing in the lockfile changed, because the recorded checksum was right all along — the cached bytes weren't.
Verify the install is clean and keep it from recurring
Prove it with an immutable install — the flag CI should use anyway:
yarn install --immutable➤ YN0000: · Done in 3s 421ms--immutable aborts "if the lockfile was to be modified," so a clean, checksum-matching install exits 0 with no lock changes. To stop the mismatch returning: commit yarn.lock and never hand-edit the cache; pin one Yarn version with packageManager so everyone resolves hashes the same way; and in CI use --immutable (and --immutable-cache on Berry) so a drifting checksum fails the pipeline loudly instead of being silently "fixed."
How this differs from ERESOLVE and a registry 404
A checksum mismatch is about bytes that changed. An ERESOLVE (npm) or a Yarn peer-dependency error is about versions that can't be reconciled — the resolver, not the download. And a 404 Not Found / "couldn't find package" means the version doesn't exist in the registry at all. If the message says the archive or integrity "doesn't match," it's this article; if it says a version can't be resolved or found, no cache wipe will help.
Related questions
Should I just delete yarn.lock to fix this?
No. That throws away every pinned version, not just the one bad checksum, and invites a fresh set of resolution surprises. Refetch the offending package (reset on Berry, yarn cache clean on Classic) or regenerate hashes with --update-checksums, and keep the lockfile in source control.
What's the difference between YARN_CHECKSUM_BEHAVIOR=reset and =update?
reset purges the cached file and re-downloads it, keeping the lockfile's hash as the source of truth. update rewrites the lockfile to match whatever is cached. Use reset when you trust the lock; use update only when you deliberately trust the new bytes.
The error only happens in CI, not on my laptop.
Your local cache holds a good copy; CI restored a corrupt or mismatched one. Bust the CI cache (reset on Berry, yarn cache clean on Classic) and re-run. If it persists across fresh runs, your CI registry differs from where the lock was generated.
Is --update-checksums safe?
Only if you trust where the new bytes came from. It writes the computed hash into yarn.lock, so a tampered tarball would become the new 'expected' value. Prefer reset or a plain refetch unless you've knowingly changed registries or mirrors.
Do I need to clear node_modules on Berry too?
Usually not. Berry installs from .yarn/cache, so purging the cache entry with reset is enough. On Classic, remove node_modules along with the cache, because the .yarn-integrity marker written there can go stale and re-trigger the check.
References
Haneul Seo
Infrastructure engineer · 10+ years running Linux fleets
More in this category
Git: fatal: detected dubious ownership in repository
Since the CVE-2022-24765 fix in Git 2.35.2, Git refuses to read a repository whose working tree or .git directory is owned by a different user than the one running the command. It shows up in containers, CI jobs, sudo sessions and shared drives. Fix the ownership if the repo should be yours, or add the exact path to safe.directory in your global config — never in the repo's own config, which Git ignores for this.
Kubernetes: Internal error occurred: failed calling webhook
An admission webhook sits in front of your write, the API server could not get an answer out of it, and failurePolicy: Fail turned that silence into a rejection. The tail of the message is the whole diagnosis: context deadline exceeded means the call went nowhere, no endpoints available means nothing is running, and an x509 line means the API server does not trust the webhook's certificate. Each has a different fix, and none of them is your manifest.
MySQL: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
A statement waited the full innodb_lock_wait_timeout for a row lock another transaction is still holding, and gave up. sys.innodb_lock_waits names the blocking session and hands you the KILL statement, and a blocking_query of NULL means the blocker is idle on an open transaction. The detail most retry loops get wrong: by default only the timed-out statement is rolled back, so your transaction is still open and still holds every lock it took earlier.
Redis: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk
Reads keep working and every write is rejected, because the last background save failed and stop-writes-on-bgsave-error defaults to yes. The log names the real cause — no space, a dir the redis user can't write, a read-only mount at rename time, or fork failing with Cannot allocate memory. Fix the cause, run one BGSAVE, and writes come back on their own with no restart: rdb_last_bgsave_status flips from err to ok. Setting stop-writes-on-bgsave-error no restores writes instantly but leaves the snapshot broken, so treat it as a deliberate trade, not the fix.
Node.js: FATAL ERROR: Reached heap limit — JavaScript heap out of memory (exit 134)
The V8 heap has its own ceiling, derived from system memory and the Node release, and it is often far below the RAM you have; when a build or server reaches it, V8 aborts with FATAL ERROR: Reached heap limit and exit code 134. Read the real limit with v8.getHeapStatistics().heap_size_limit, then raise it with --max-old-space-size (in MiB) or NODE_OPTIONS for a large workload, size it below the cgroup limit inside containers, and use --heapsnapshot-near-heap-limit to catch a leak in a long-running process. Exit 137 with no FATAL ERROR line is a container kill, not this.
Docker: "exec format error" when the container starts — wrong-platform image, no emulator, or a script with no shebang
The container exits on its first instruction with exec format error — the kernel's ENOEXEC, meaning the file exists but cannot be executed here. In practice that is an image built on one CPU architecture (an Apple-silicon Mac produces linux/arm64) and run on another (an x86_64 server) with no QEMU handler registered in binfmt_misc, or an entrypoint script whose first line is not a shebang. uname -m, docker image inspect and ls /proc/sys/fs/binfmt_misc tell the causes apart; the fix is an explicit docker buildx build --platform (or a manifest list for both), QEMU registration or --platform when you mean to emulate, and a #!/bin/sh line for the script.