{"record":{"id":"a96a4dd8eb1b1072","repo":"pnpm/pnpm","slug":"broken-lockfile","errorCode":"BROKEN_LOCKFILE","errorMessage":"The lockfile at \"${opts.lockfileDir}/${WANTED_LOCKFILE}\" is broken: it is empty","messagePattern":"The lockfile at \"(.+?)/(.+?)\" is broken: it is empty","errorType":"exception","errorClass":"PnpmError","httpStatus":null,"severity":"error","filePath":"pnpm11/installing/context/src/readLockfiles.ts","lineNumber":113,"sourceCode":"    }\n    fileReads.push(Promise.resolve(undefined))\n  }\n  fileReads.push(\n    (async () => {\n      try {\n        return await readCurrentLockfile(opts.internalPnpmDir, lockfileOpts)\n      } catch (err: any) { // eslint-disable-line\n        logger.warn({\n          message: `Ignoring broken lockfile at ${opts.internalPnpmDir}: ${err.message as string}`,\n          prefix: opts.lockfileDir,\n        })\n        return undefined\n      }\n    })()\n  )\n  const files = await Promise.all<LockfileObject | null | undefined>(fileReads)\n  if (opts.frozenLockfile && wantedLockfileFileExists && files[0] == null) {\n    throw new PnpmError('BROKEN_LOCKFILE', `The lockfile at \"${path.join(opts.lockfileDir, WANTED_LOCKFILE)}\" is broken: it is empty`)\n  }\n  const sopts = {\n    autoInstallPeers: opts.autoInstallPeers,\n    excludeLinksFromLockfile: opts.excludeLinksFromLockfile,\n    lockfileVersion: wantedLockfileVersion,\n    peersSuffixMaxLength: opts.peersSuffixMaxLength,\n  }\n  const importerIds = opts.projects.map((importer) => importer.id)\n  const currentLockfile = files[1] ?? createLockfileObject(importerIds, sopts)\n  for (const importerId of importerIds) {\n    if (!currentLockfile.importers[importerId]) {\n      currentLockfile.importers[importerId] = {\n        specifiers: {},\n      }\n    }\n  }\n  const existsWantedLockfile = files[0] != null\n  const existsCurrentLockfile = files[1] != null","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/installing/context/src/readLockfiles.ts#L95-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: CI frozen install fails, pnpm-lock.yaml is 0 bytes\ncat pnpm-lock.yaml   # (empty)\n\n# after: regenerate and commit a real lockfile\npnpm install --no-frozen-lockfile\ngit add pnpm-lock.yaml && git commit -m 'chore: restore pnpm-lock.yaml'","handlingStrategy":"validation","validationCode":"// CI pre-flight: refuse a frozen install when the lockfile is empty\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\n\nexport function lockfileIsReadable (lockfileDir: string): boolean {\n  try {\n    const content = readFileSync(join(lockfileDir, 'pnpm-lock.yaml'), 'utf8').trim()\n    return content.length > 0 && content.includes('lockfileVersion:')\n  } catch {\n    return false\n  }\n}\n// if (!lockfileIsReadable(lockfileDir)) throw new Error('pnpm-lock.yaml is missing or empty')","typeGuard":"import util from 'util'\nimport { PnpmError } from '@pnpm/error'\n\nexport const isBrokenLockfileError = (err: unknown): err is PnpmError =>\n  util.types.isNativeError(err) && (err as PnpmError).code === 'BROKEN_LOCKFILE'","tryCatchPattern":"try {\n  await run('pnpm', ['install', '--frozen-lockfile'])\n} catch (err) {\n  if (isBrokenLockfileError(err)) {\n    // regenerate the lockfile, then retry without frozen\n    await run('pnpm', ['install', '--no-frozen-lockfile'])\n  } else {\n    throw err\n  }\n}","preventionTips":["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"],"tags":["lockfile","frozen-lockfile","ci","git"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}