pnpm/pnpm · error · PnpmError
BROKEN_LOCKFILE
BROKEN_LOCKFILE
Error message
The lockfile at "${opts.lockfileDir}/${WANTED_LOCKFILE}" is broken: it is empty What it means
Thrown by readLockfiles when a frozen install (--frozen-lockfile, the CI default) finds that pnpm-lock.yaml exists on disk but parsed to null, typically a zero-byte file. A normal install would silently regenerate the file, but a frozen install promises not to modify the lockfile, so an unusable wanted lockfile is fatal.
Source
Thrown at pnpm11/installing/context/src/readLockfiles.ts:113
}
fileReads.push(Promise.resolve(undefined))
}
fileReads.push(
(async () => {
try {
return await readCurrentLockfile(opts.internalPnpmDir, lockfileOpts)
} catch (err: any) { // eslint-disable-line
logger.warn({
message: `Ignoring broken lockfile at ${opts.internalPnpmDir}: ${err.message as string}`,
prefix: opts.lockfileDir,
})
return undefined
}
})()
)
const files = await Promise.all<LockfileObject | null | undefined>(fileReads)
if (opts.frozenLockfile && wantedLockfileFileExists && files[0] == null) {
throw new PnpmError('BROKEN_LOCKFILE', `The lockfile at "${path.join(opts.lockfileDir, WANTED_LOCKFILE)}" is broken: it is empty`)
}
const sopts = {
autoInstallPeers: opts.autoInstallPeers,
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
lockfileVersion: wantedLockfileVersion,
peersSuffixMaxLength: opts.peersSuffixMaxLength,
}
const importerIds = opts.projects.map((importer) => importer.id)
const currentLockfile = files[1] ?? createLockfileObject(importerIds, sopts)
for (const importerId of importerIds) {
if (!currentLockfile.importers[importerId]) {
currentLockfile.importers[importerId] = {
specifiers: {},
}
}
}
const existsWantedLockfile = files[0] != null
const existsCurrentLockfile = files[1] != nullView on GitHub (pinned to 5b11d3a15b)
Solutions
- Run `pnpm install --no-frozen-lockfile` (or plain `pnpm install` locally) to regenerate pnpm-lock.yaml and commit it
- Check the file is not empty: `wc -c pnpm-lock.yaml`
- If a merge is in progress, resolve the lockfile conflict and run `pnpm install` before pushing
- Verify pnpm-lock.yaml is not listed in .gitignore and was actually pushed
Example fix
# before: CI frozen install fails, pnpm-lock.yaml is 0 bytes cat pnpm-lock.yaml # (empty) # after: regenerate and commit a real lockfile pnpm install --no-frozen-lockfile git add pnpm-lock.yaml && git commit -m 'chore: restore pnpm-lock.yaml'
Defensive patterns
Strategy: validation
Validate before calling
// CI pre-flight: refuse a frozen install when the lockfile is empty
import { readFileSync } from 'fs'
import { join } from 'path'
export function lockfileIsReadable (lockfileDir: string): boolean {
try {
const content = readFileSync(join(lockfileDir, 'pnpm-lock.yaml'), 'utf8').trim()
return content.length > 0 && content.includes('lockfileVersion:')
} catch {
return false
}
}
// if (!lockfileIsReadable(lockfileDir)) throw new Error('pnpm-lock.yaml is missing or empty') Type guard
import util from 'util'
import { PnpmError } from '@pnpm/error'
export const isBrokenLockfileError = (err: unknown): err is PnpmError =>
util.types.isNativeError(err) && (err as PnpmError).code === 'BROKEN_LOCKFILE' Try / catch
try {
await run('pnpm', ['install', '--frozen-lockfile'])
} catch (err) {
if (isBrokenLockfileError(err)) {
// regenerate the lockfile, then retry without frozen
await run('pnpm', ['install', '--no-frozen-lockfile'])
} else {
throw err
}
} Prevention
- Always commit a non-empty pnpm-lock.yaml together with package.json changes
- After merge conflicts, run pnpm install locally before pushing to verify the lockfile
- Add a CI check that pnpm-lock.yaml contains 'lockfileVersion:' before the frozen install step
When it happens
Trigger: Running `pnpm install --frozen-lockfile` (or any install in CI, where frozenLockfile defaults to true) while <lockfileDir>/pnpm-lock.yaml exists but is empty or parses to null, so files[0] (the wanted lockfile) is null and wantedLockfileFileExists is true.
Common situations: Lockfile truncated by an interrupted git operation or a resolved-then-emptied merge conflict; a disk-full write; an empty pnpm-lock.yaml accidentally committed and then checked out in CI.
Related errors
- NO_LOCKFILE
- LOCKFILE_CONFIG_MISMATCH
- FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE
- OUTDATED_LOCKFILE
- AUDIT_NO_LOCKFILE
AI-assisted analysis of pnpm/pnpm@5b11d3a15b (2026-08-16).
Data as JSON: /api/errors/a96a4dd8eb1b1072.
Report an issue: GitHub.