{"record":{"id":"a004ea9dfcc7d30e","repo":"pnpm/pnpm","slug":"err-pnpm-git-checkout-failed","errorCode":"ERR_PNPM_GIT_CHECKOUT_FAILED","errorMessage":"received commit ${receivedCommit.trim()} does not match expected value ${resolution.commit}","messagePattern":"received commit (.+?) does not match expected value (.+?)","errorType":"exception","errorClass":"PnpmError","httpStatus":null,"severity":"error","filePath":"pnpm11/fetching/git-fetcher/src/index.ts","lineNumber":49,"sourceCode":"      throw new PnpmError('INVALID_GIT_COMMIT', `Invalid git commit hash \"${resolution.commit}\" for repository \"${resolution.repo}\". Expected a 40-character hexadecimal SHA.`)\n    }\n    const tempLocation = await cafs.tempDir()\n    try {\n      if (allowedHosts.size > 0 && shouldUseShallow(resolution.repo, allowedHosts)) {\n        await execGit(['init'], { cwd: tempLocation })\n        await execGit(['remote', 'add', 'origin', resolution.repo], { cwd: tempLocation })\n        await execGit(['fetch', '--depth', '1', 'origin', resolution.commit], { cwd: tempLocation })\n      } else {\n        await execGit(['clone', resolution.repo, tempLocation])\n      }\n    } catch (err: unknown) {\n      assert(util.types.isNativeError(err))\n      throw gitFetchError(err, resolution.repo, opts.pkg?.name)\n    }\n    await execGit(['checkout', resolution.commit], { cwd: tempLocation })\n    const receivedCommit = await execGit(['rev-parse', 'HEAD'], { cwd: tempLocation })\n    if (receivedCommit.trim() !== resolution.commit) {\n      throw new PnpmError('GIT_CHECKOUT_FAILED', `received commit ${receivedCommit.trim()} does not match expected value ${resolution.commit}`)\n    }\n    let pkgDir: string\n    try {\n      const prepareResult = await preparePackage({\n        allowBuild: opts.allowBuild,\n        ignoreScripts: createOpts.ignoreScripts,\n        pkgResolutionId: createGitHostedPkgId(resolution),\n        unsafePerm: createOpts.unsafePerm,\n        userAgent: createOpts.userAgent,\n      }, tempLocation, resolution.path ?? '')\n      pkgDir = prepareResult.pkgDir\n      if (ignoreScripts && prepareResult.shouldBeBuilt) {\n        globalWarn(`The git-hosted package fetched from \"${resolution.repo}\" has to be built but the build scripts were ignored.`)\n      }\n    } catch (err: unknown) {\n      assert(util.types.isNativeError(err))\n      err.message = `Failed to prepare git-hosted package fetched from \"${resolution.repo}\": ${err.message}`\n      throw err","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/pnpm/pnpm/blob/6261b7f388016d57ca6b90340342411cd1d0d00f/pnpm11/fetching/git-fetcher/src/index.ts#L31-L67","documentation":"After cloning (or shallow-fetching) and checking out resolution.commit, the git fetcher reads back HEAD via git rev-parse and requires an exact string match with the pinned commit. A mismatch means the working copy did not land on the pinned SHA — typically a shallow fetch that could not honor the exact commit, or upstream history rewritten between resolution and fetch.","triggerScenarios":"The repo host is in gitShallowHosts and git fetch --depth 1 origin <commit> yields a different HEAD; the upstream force-pushed between resolve-time and fetch-time; a corrupted local git cache served the wrong commit.","commonSituations":"Racing an upstream force-push during CI; exotic git hosting with partial shallow-fetch support; rare flaky mirrors.","solutions":["Re-run the install — if the mismatch was a transient race, a fresh resolve+fetch succeeds","Update the pinned commit in the lockfile to the current upstream SHA (regenerate the lockfile)","Remove the host from gitShallowHosts (config) so a full clone is used instead of a shallow fetch"],"exampleFix":"# before: pinned commit no longer reachable after upstream force-push\n$ pnpm install  # ERR_PNPM_GIT_CHECKOUT_FAILED: received commit abc... does not match expected def...\n\n# after: re-resolve to the rewritten history\n$ rm pnpm-lock.yaml && pnpm install","handlingStrategy":"try-catch","validationCode":"import { execa } from 'execa'\n\n// Confirm the pinned commit is still the upstream tip-ish ref before fetching\nasync function assertCommitReachable (repo: string, commit: string): Promise<void> {\n  const { stdout } = await execa('git', ['ls-remote', repo])\n  const reachable = stdout.split('\\n').some(line => line.startsWith(commit))\n  if (!reachable) {\n    throw new Error(`${commit} no longer advertised by ${repo}; history was probably rewritten — re-resolve`)\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fetchers.git(cafs, resolution, opts)\n} catch (err) {\n  if (err instanceof PnpmError && err.code === 'ERR_PNPM_GIT_CHECKOUT_FAILED') {\n    // Either a force-push race (re-resolve and retry once) or shallow-fetch weirdness\n    const fresh = await reResolveGitDependency(resolution.repo)\n    if (fresh.commit === resolution.commit) throw err // deterministic — do not loop\n    return fetchers.git(cafs, { ...resolution, commit: fresh.commit }, opts)\n  }\n  throw err\n}","preventionTips":["Prefer full clones over shallow fetches for git deps from hosts with unreliable commit fetching (drop them from gitShallowHosts)","Re-lock git dependencies after upstream force-pushes instead of trusting old SHAs","Retry once on mismatch — races between resolve and fetch are the common benign cause"],"tags":["git","dependency","shallow-fetch","force-push"],"backgroundTag":"git-commit-mismatch","analyzedSha":"6261b7f388016d57ca6b90340342411cd1d0d00f","analyzedAt":"2026-08-17T18:30:54.750Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}