vercel/turborepo · error · ConvertError

package_manager-unexpected

package_manager-unexpected

Error message

Not a pnpm project

What it means

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".

Source

Thrown at packages/turbo-workspaces/src/managers/pnpm.ts:62

  const lockFile = path.join(args.workspaceRoot, PACKAGE_MANAGER_DETAILS.lock);
  const workspaceFile = path.join(args.workspaceRoot, "pnpm-workspace.yaml");
  const packageManager = getWorkspacePackageManager({
    workspaceRoot: args.workspaceRoot
  });
  return (
    fs.existsSync(lockFile) ||
    fs.existsSync(workspaceFile) ||
    packageManager === PACKAGE_MANAGER_DETAILS.name
  );
}

/**
  Read workspace data from pnpm workspaces into generic format
*/
async function read(args: ReadArgs): Promise<Project> {
  const isPnpm = await detect(args);
  if (!isPnpm) {
    throw new ConvertError("Not a pnpm project", {
      type: "package_manager-unexpected"
    });
  }

  const { name, description } = getWorkspaceInfo(args);
  return {
    name,
    description,
    packageManager: PACKAGE_MANAGER_DETAILS.name,
    paths: expandPaths({
      root: args.workspaceRoot,
      lockFile: PACKAGE_MANAGER_DETAILS.lock,
      workspaceConfig: "pnpm-workspace.yaml"
    }),
    workspaceData: {
      globs: getPnpmWorkspaces(args),
      workspaces: expandWorkspaces({
        workspaceGlobs: getPnpmWorkspaces(args),

View on GitHub (pinned to 9f94a7d215)

Solutions

  1. Ensure pnpm-lock.yaml or pnpm-workspace.yaml exists at the root (run `pnpm install` to regenerate the lockfile), then retry
  2. Add "packageManager": "pnpm@<version>" or devEngines.packageManager: { "name": "pnpm", "version": "<range>" } to root package.json so detection does not rely on lockfiles
  3. Prefer getWorkspaceDetails({ root }) over MANAGERS.pnpm.read so the manager matching the actual files wins
  4. Re-run the migration after any concurrent checkout/clean finishes so detection and read agree

Example fix

// before
const project = await MANAGERS.pnpm.read({ workspaceRoot: root }); // throws if no markers

// after
if (await MANAGERS.pnpm.detect({ workspaceRoot: root })) {
  const project = await MANAGERS.pnpm.read({ workspaceRoot: root });
}
Defensive patterns

Strategy: validation

Validate before calling

import { MANAGERS } from "turbo-workspaces";
import { existsSync } from "node:fs";
import path from "node:path";

const markers = ["pnpm-lock.yaml", "pnpm-workspace.yaml"].map(f =>
  path.join(root, f)
);
if (!markers.some(existsSync)) {
  throw new Error(`${root} has no pnpm-lock.yaml / pnpm-workspace.yaml`);
}
const project = await MANAGERS.pnpm.read({ workspaceRoot: root });

Type guard

import { ConvertError } from "turbo-workspaces";

function isPackageManagerUnexpected(err: unknown): err is ConvertError {
  return err instanceof ConvertError && err.type === "package_manager-unexpected";
}

Try / catch

try {
  const project = await MANAGERS.pnpm.read({ workspaceRoot: root });
} catch (err) {
  if (err instanceof ConvertError && err.type === "package_manager-unexpected") {
    project = await getWorkspaceDetails({ root });
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of vercel/turborepo@9f94a7d215 (2026-08-16). Data as JSON: /api/errors/30ad6692090bb26b. Report an issue: GitHub.