{"record":{"id":"a2b01704c5bb086e","repo":"vercel/turborepo","slug":"package-manager-unexpected-a2b017","errorCode":"package_manager-unexpected","errorMessage":"Not an npm project","messagePattern":"Not an npm project","errorType":"exception","errorClass":"ConvertError","httpStatus":null,"severity":"error","filePath":"packages/turbo-workspaces/src/managers/npm.ts","lineNumber":57,"sourceCode":" */\n// eslint-disable-next-line @typescript-eslint/require-await -- must match the detect type signature\nasync function detect(args: DetectArgs): Promise<boolean> {\n  const lockFile = path.join(args.workspaceRoot, PACKAGE_MANAGER_DETAILS.lock);\n  const packageManager = getWorkspacePackageManager({\n    workspaceRoot: args.workspaceRoot\n  });\n  return (\n    fs.existsSync(lockFile) || packageManager === PACKAGE_MANAGER_DETAILS.name\n  );\n}\n\n/**\n  Read workspace data from npm workspaces into generic format\n*/\nasync function read(args: ReadArgs): Promise<Project> {\n  const isNpm = await detect(args);\n  if (!isNpm) {\n    throw new ConvertError(\"Not an npm project\", {\n      type: \"package_manager-unexpected\"\n    });\n  }\n\n  const packageJson = getPackageJson(args);\n  const { name, description } = getWorkspaceInfo(args);\n  const workspaceGlobs = parseWorkspacePackages({\n    workspaces: packageJson.workspaces\n  });\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    }),\n    workspaceData: {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/packages/turbo-workspaces/src/managers/npm.ts#L39-L75","documentation":"The npm manager's read() guard: it re-runs detect (package-lock.json exists or packageManager === 'npm') and throws ConvertError type package_manager-unexpected if false. Like the other managers, read() is meant to be called only after getWorkspaceDetails' detect loop has selected npm; calling it on a non-npm workspace is an API contract violation, not a user config problem.","triggerScenarios":"Directly calling managers/npm read({ workspaceRoot }) on a workspace without package-lock.json and without \"packageManager\": \"npm@...\" - e.g. custom scripts importing the manager module, or the lockfile being absent/gitignored so detection cannot succeed.","commonSituations":"Migration tooling that hardcodes the npm manager instead of detecting, fresh repos where npm install never ran (no lockfile), lockfiles excluded via .gitignore or .npmrc package-lock=false, or tests without fixtures.","solutions":["Go through getWorkspaceDetails so detection picks the correct manager","If calling read directly, always pair with detect() and skip when false","Generate the lockfile: run npm install once and commit package-lock.json (ensure package-lock is not disabled)","Declare \"packageManager\": \"npm@x.y.z\" in package.json for lockfile-free detection"],"exampleFix":"// before\nconst project = await MANAGERS.npm.read({ workspaceRoot }); // throws \"Not an npm project\"\n\n// after\nif (await MANAGERS.npm.detect({ workspaceRoot })) {\n  const project = await MANAGERS.npm.read({ workspaceRoot });\n}","handlingStrategy":"type-guard","validationCode":"if (!(await MANAGERS.npm.detect({ workspaceRoot }))) {\n  throw new Error(\"Workspace is not an npm project (no package-lock.json, no packageManager field)\");\n}\nconst project = await MANAGERS.npm.read({ workspaceRoot });","typeGuard":"async function readIfNpm(args: { workspaceRoot: string }): Promise<Project | null> {\n  return (await MANAGERS.npm.detect(args)) ? MANAGERS.npm.read(args) : null;\n}","tryCatchPattern":"try {\n  const project = await MANAGERS.npm.read({ workspaceRoot });\n} catch (err) {\n  if (err instanceof ConvertError && err.type === \"package_manager-unexpected\") {\n    const project = await getWorkspaceDetails({ root: workspaceRoot }); // correct detect/read pairing\n  } else throw err;\n}","preventionTips":["Route through getWorkspaceDetails instead of hardcoded manager modules","Ensure package-lock.json exists (run npm install once) before reading as npm","Declare packageManager in package.json for deterministic detection"],"tags":["package-manager","npm","detection","api-misuse","turbo-workspaces"],"backgroundTag":"package-manager-detection-failed","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}