{"record":{"id":"dc371c3227005ef2","repo":"pnpm/pnpm","slug":"unexpected-store","errorCode":"UNEXPECTED_STORE","errorMessage":"Unexpected store location","messagePattern":"Unexpected store location","errorType":"exception","errorClass":"UnexpectedStoreError","httpStatus":null,"severity":"error","filePath":"pnpm11/installing/deps-installer/src/install/checkCompatibility/index.ts","lineNumber":31,"sourceCode":"    storeDir: string\n    modulesDir: string\n    virtualStoreDir: string\n  }\n): void {\n  if (!modules.layoutVersion || modules.layoutVersion !== LAYOUT_VERSION) {\n    throw new ModulesBreakingChangeError({\n      modulesPath: opts.modulesDir,\n    })\n  }\n  // Important: comparing paths with path.relative()\n  // is the only way to compare paths correctly on Windows\n  // as of Node.js 4-9\n  // See related issue: https://github.com/pnpm/pnpm/issues/996\n  if (\n    !modules.storeDir ||\n    path.relative(modules.storeDir, opts.storeDir) !== '' && path.relative(modules.storeDir, path.join(opts.storeDir, '../v3')) !== ''\n  ) {\n    throw new UnexpectedStoreError({\n      actualStorePath: opts.storeDir,\n      expectedStorePath: modules.storeDir,\n      modulesDir: opts.modulesDir,\n    })\n  }\n  if (modules.virtualStoreDir && path.relative(modules.virtualStoreDir, opts.virtualStoreDir) !== '') {\n    throw new UnexpectedVirtualStoreDirError({\n      actual: opts.virtualStoreDir,\n      expected: modules.virtualStoreDir,\n      modulesDir: opts.modulesDir,\n    })\n  }\n}\n","sourceCodeStart":13,"sourceCodeEnd":45,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/installing/deps-installer/src/install/checkCompatibility/index.ts#L13-L45","documentation":"node_modules/.modules.yaml records the store directory the packages were hard-linked from. checkCompatibility compares it with the currently resolved store using path.relative (the only correct way to compare paths on Windows, per the linked issue) and also tolerates the legacy '.../v3' sibling store. A missing recorded store or a different location aborts the install because linking from a different store would break the hard-link layout.","triggerScenarios":"Install into node_modules where the recorded storeDir is set but matches neither the current store path nor path.join(storeDir, '../v3') via path.relative, or modules.storeDir is absent.","commonSituations":"Changed `store-dir` in .npmrc or pnpm-workspace.yaml; a different HOME/user running the install (the default store lives under the user's home); project moved between CI runners or containers with different mount points or drives.","solutions":["Run `pnpm install --force` to re-link node_modules from the current store","If the store moved for a reason you control, restore the previous `store-dir` setting instead","Set an explicit shared `store-dir` (CI, Docker volume) so the resolved path never drifts","As a last resort, delete node_modules and reinstall"],"exampleFix":"# before: store location changed after editing store-dir\npnpm install   # ERR_PNMP_UNEXPECTED_STORE\n\n# after: either restore the old store path or force a re-link\npnpm config set store-dir /previous/store/path   # option A\npnpm install --force                               # option B","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'fs'\nimport { join, relative } from 'path'\nimport { parse } from 'yaml'\nimport { execSync } from 'child_process'\n\n// Verify the store that built node_modules is the store pnpm resolves now\nexport function storeMatches (lockfileDir: string): boolean {\n  const modules = parse(readFileSync(join(lockfileDir, 'node_modules', '.modules.yaml'), 'utf8')) as { storeDir?: string }\n  const currentStore = execSync('pnpm store path', { encoding: 'utf8' }).trim()\n  return modules.storeDir != null && (\n    relative(modules.storeDir, currentStore) === '' ||\n    relative(modules.storeDir, join(currentStore, '../v3')) === ''\n  )\n}","typeGuard":"import util from 'util'\nimport { PnpmError } from '@pnpm/error'\n\nexport const isUnexpectedStoreError = (err: unknown): err is PnpmError =>\n  util.types.isNativeError(err) && (err as PnpmError).code === 'UNEXPECTED_STORE'","tryCatchPattern":"try {\n  await mutateModules(mutations, opts)\n} catch (err) {\n  if (isUnexpectedStoreError(err)) {\n    // err.expected is the recorded store dir; re-point config there, or purge and retry\n    await rimraf(join(opts.lockfileDir, 'node_modules'))\n    return mutateModules(mutations, opts)\n  }\n  throw err\n}","preventionTips":["Pin `store-dir` explicitly in CI and Docker so the resolved path never drifts","Do not move a project with its node_modules across users, machines, or mount points","After changing store-dir, expect a one-time `pnpm install --force` to re-link"],"tags":["store","node-modules","config","compatibility"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}