{"record":{"id":"30ad6692090bb26b","repo":"vercel/turborepo","slug":"package-manager-unexpected-30ad66","errorCode":"package_manager-unexpected","errorMessage":"Not a pnpm project","messagePattern":"Not a pnpm project","errorType":"exception","errorClass":"ConvertError","httpStatus":null,"severity":"error","filePath":"packages/turbo-workspaces/src/managers/pnpm.ts","lineNumber":62,"sourceCode":"  const lockFile = path.join(args.workspaceRoot, PACKAGE_MANAGER_DETAILS.lock);\n  const workspaceFile = path.join(args.workspaceRoot, \"pnpm-workspace.yaml\");\n  const packageManager = getWorkspacePackageManager({\n    workspaceRoot: args.workspaceRoot\n  });\n  return (\n    fs.existsSync(lockFile) ||\n    fs.existsSync(workspaceFile) ||\n    packageManager === PACKAGE_MANAGER_DETAILS.name\n  );\n}\n\n/**\n  Read workspace data from pnpm workspaces into generic format\n*/\nasync function read(args: ReadArgs): Promise<Project> {\n  const isPnpm = await detect(args);\n  if (!isPnpm) {\n    throw new ConvertError(\"Not a pnpm project\", {\n      type: \"package_manager-unexpected\"\n    });\n  }\n\n  const { name, description } = getWorkspaceInfo(args);\n  return {\n    name,\n    description,\n    packageManager: PACKAGE_MANAGER_DETAILS.name,\n    paths: expandPaths({\n      root: args.workspaceRoot,\n      lockFile: PACKAGE_MANAGER_DETAILS.lock,\n      workspaceConfig: \"pnpm-workspace.yaml\"\n    }),\n    workspaceData: {\n      globs: getPnpmWorkspaces(args),\n      workspaces: expandWorkspaces({\n        workspaceGlobs: getPnpmWorkspaces(args),","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/vercel/turborepo/blob/9f94a7d215b3881942527dd526afa3fc650869d5/packages/turbo-workspaces/src/managers/pnpm.ts#L44-L80","documentation":"Thrown by the pnpm manager handler's read() in turbo-workspaces when pnpm's detect() returns false. Detection means: pnpm-lock.yaml exists OR pnpm-workspace.yaml exists OR the packageManager/devEngines.packageManager field declares pnpm. Note that a pnpm-workspace.yaml alone is enough to make detect() true, so read() can pass detection even with no lockfile. ConvertError type \"package_manager-unexpected\".","triggerScenarios":"Calling MANAGERS.pnpm.read({ workspaceRoot }) directly on a root that has none of pnpm-lock.yaml, pnpm-workspace.yaml, or a pnpm declaration; via getWorkspaceDetails()/convert() only when those markers disappear between detect() and read() (concurrent git clean, lockfile deletion, or package.json rewrite during the run).","commonSituations":"pnpm-lock.yaml and pnpm-workspace.yaml gitignored or wiped by a clean script, leaving only an ambiguous project; the packageManager field removed when corepack was disabled or the field stripped during a merge; passing a wrong workspaceRoot (e.g. the git toplevel when the pnpm root is a subdirectory); hand-rolling detection with only `which pnpm` and assuming a global pnpm implies a pnpm project.","solutions":["Ensure pnpm-lock.yaml or pnpm-workspace.yaml exists at the root (run `pnpm install` to regenerate the lockfile), then retry","Add \"packageManager\": \"pnpm@<version>\" or devEngines.packageManager: { \"name\": \"pnpm\", \"version\": \"<range>\" } to root package.json so detection does not rely on lockfiles","Prefer getWorkspaceDetails({ root }) over MANAGERS.pnpm.read so the manager matching the actual files wins","Re-run the migration after any concurrent checkout/clean finishes so detection and read agree"],"exampleFix":"// before\nconst project = await MANAGERS.pnpm.read({ workspaceRoot: root }); // throws if no markers\n\n// after\nif (await MANAGERS.pnpm.detect({ workspaceRoot: root })) {\n  const project = await MANAGERS.pnpm.read({ workspaceRoot: root });\n}","handlingStrategy":"validation","validationCode":"import { MANAGERS } from \"turbo-workspaces\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\nconst markers = [\"pnpm-lock.yaml\", \"pnpm-workspace.yaml\"].map(f =>\n  path.join(root, f)\n);\nif (!markers.some(existsSync)) {\n  throw new Error(`${root} has no pnpm-lock.yaml / pnpm-workspace.yaml`);\n}\nconst project = await MANAGERS.pnpm.read({ workspaceRoot: root });","typeGuard":"import { ConvertError } from \"turbo-workspaces\";\n\nfunction isPackageManagerUnexpected(err: unknown): err is ConvertError {\n  return err instanceof ConvertError && err.type === \"package_manager-unexpected\";\n}","tryCatchPattern":"try {\n  const project = await MANAGERS.pnpm.read({ workspaceRoot: root });\n} catch (err) {\n  if (err instanceof ConvertError && err.type === \"package_manager-unexpected\") {\n    project = await getWorkspaceDetails({ root });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Commit pnpm-lock.yaml and pnpm-workspace.yaml so every checkout is detectable","Add \"packageManager\": \"pnpm@<version>\" for lockfile-independent detection","Use getWorkspaceDetails unless you have specifically confirmed the project is pnpm","Do not run git clean/checkouts concurrently with migration"],"tags":["pnpm","workspaces","package-manager-detection","migration","convert"],"backgroundTag":"wrong-package-manager","analyzedSha":"9f94a7d215b3881942527dd526afa3fc650869d5","analyzedAt":"2026-08-16T19:47:29.531Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}