{"record":{"id":"78999d2b5f1072ab","repo":"pnpm/pnpm","slug":"invalid-git-commit","errorCode":"INVALID_GIT_COMMIT","errorMessage":"Invalid git commit hash \"${resolution.commit}\" for repository \"${resolution.repo}\". Expected a 40-character hexadecimal SHA.","messagePattern":"Invalid git commit hash \"(.+?)\" for repository \"(.+?)\"\\. Expected a 40-character hexadecimal SHA\\.","errorType":"exception","errorClass":"PnpmError","httpStatus":null,"severity":"error","filePath":"pnpm11/fetching/git-fetcher/src/index.ts","lineNumber":31,"sourceCode":"import { addFilesFromDir } from '@pnpm/worker'\nimport { rimraf } from '@zkochan/rimraf'\nimport { safeExeca as execa } from 'execa'\n\nexport interface CreateGitFetcherOptions {\n  gitShallowHosts?: string[]\n  storeIndex: StoreIndex\n  unsafePerm?: boolean\n  userAgent?: string\n  ignoreScripts?: boolean\n}\n\nexport function createGitFetcher (createOpts: CreateGitFetcherOptions): { git: GitFetcher } {\n  const allowedHosts = new Set(createOpts?.gitShallowHosts ?? [])\n  const ignoreScripts = createOpts.ignoreScripts ?? false\n\n  const gitFetcher: GitFetcher = async (cafs, resolution, opts) => {\n    if (!isValidCommitHash(resolution.commit)) {\n      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}`)","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/fetching/git-fetcher/src/index.ts#L13-L49","documentation":"Before touching the network, the git fetcher validates resolution.commit: it must be a full 40-character hexadecimal SHA. Anything else — a branch name like 'main', a tag, or an abbreviated SHA — fails isValidCommitHash. This is both a correctness requirement (checkout needs an immutable ref) and a safety check on the value passed to git.","triggerScenarios":"resolution.commit is not a 40-hex SHA: hand-edited lockfiles storing branch or tag names or short SHAs; custom tooling or resolvers emitting non-SHA commits; lockfiles produced by an incompatible pnpm version.","commonSituations":"Manually 'simplifying' lockfile git entries; scripts that rewrite lockfiles; mixed pnpm versions across a team writing different resolution shapes.","solutions":["Regenerate the lockfile so the git resolver records the full SHA: rm pnpm-lock.yaml && pnpm install","Reference git deps by tag or branch in package.json and let pnpm resolve and lock the full SHA","Never hand-edit resolution.commit fields"],"exampleFix":"# before - hand-edited lockfile\nresolution:\n  commit: main\n\n# after - locked full SHA (regenerate the lockfile)\nresolution:\n  commit: 9f8e7d6c5b4a3928172635445362718990aabbcc","handlingStrategy":"validation","validationCode":"const COMMIT_SHA = /^[0-9a-f]{40}$/i\n\nfunction isFullCommitSha (commit: string | undefined): boolean {\n  return commit != null && COMMIT_SHA.test(commit)\n}\n\nif (!isFullCommitSha(resolution.commit)) {\n  throw new Error(`resolution.commit must be a 40-char hex SHA, got: ${String(resolution.commit)}`)\n}","typeGuard":null,"tryCatchPattern":"catch code === 'INVALID_GIT_COMMIT'; respond by regenerating the lockfile rather than patching the field by hand","preventionTips":["Never hand-edit git resolutions in pnpm-lock.yaml; re-resolve instead","If tooling rewrites lockfiles, validate commit fields against a 40-hex check afterwards","Reference git deps by branch/tag in package.json and let pnpm lock the full SHA"],"tags":["git","lockfile","validation","install"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}