vercel/turborepo · error · ConvertError
package_manager-unexpected
package_manager-unexpected
Error message
Not a nub project
What it means
Thrown by the nub manager handler's read() in turbo-workspaces when nub's detect() returns false. Unlike npm/yarn/pnpm, nub detection checks ONLY the packageManager field ("packageManager": "nub@x.y.z" or devEngines.packageManager with name "nub") - the presence of nub.lock, lock.yaml, or any other lockfile alone never makes a project detect as nub. ConvertError type "package_manager-unexpected".
Source
Thrown at packages/turbo-workspaces/src/managers/nub.ts:63
bun
} as const;
/**
* nub is recognized only through the `packageManager` field in `package.json`
* (`"nub@x.y.z"`). A foreign lockfile alone does not imply nub.
*/
// eslint-disable-next-line @typescript-eslint/require-await -- must match the detect type signature
async function detect(args: DetectArgs): Promise<boolean> {
const packageManager = getWorkspacePackageManager({
workspaceRoot: args.workspaceRoot
});
return packageManager === PACKAGE_MANAGER_DETAILS.name;
}
async function read(args: ReadArgs): Promise<Project> {
const isNub = await detect(args);
if (!isNub) {
throw new ConvertError("Not a nub project", {
type: "package_manager-unexpected"
});
}
const underlying = getUnderlyingLockfileManager({
workspaceRoot: args.workspaceRoot
});
const underlyingHandler = UNDERLYING_MANAGERS[underlying];
if (await underlyingHandler.detect(args)) {
const project = await underlyingHandler.read(args);
return { ...project, packageManager: PACKAGE_MANAGER_DETAILS.name };
}
const packageJson = getPackageJson(args);
const { name, description } = getWorkspaceInfo(args);
const lockfile = getUnderlyingLockfileName({
workspaceRoot: args.workspaceRootView on GitHub (pinned to 9f94a7d215)
Solutions
- Set "packageManager": "nub@<version>" (or devEngines.packageManager: { "name": "nub", "version": "<semver range>" }) in the root package.json, then retry
- Verify the spelling - the field must match /^.+@.+$/ and the name must be exactly one of npm, pnpm, yarn, bun, nub, aube (getWorkspacePackageManager returns undefined otherwise and detection silently fails)
- Use getWorkspaceDetails({ root }) instead of MANAGERS.nub.read directly so whichever manager actually matches is the one that reads
- Avoid editing package.json while a convert/migration is in progress
Example fix
// root package.json - before
{
"name": "monorepo"
}
// after - makes nub.detect() true, so nub.read() no longer throws
{
"name": "monorepo",
"packageManager": "nub@0.9.5"
} Defensive patterns
Strategy: validation
Validate before calling
import { MANAGERS } from "turbo-workspaces";
// nub is detectable ONLY via the packageManager field - check it explicitly
const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
declaresNub = pkg.packageManager?.startsWith("nub@") ||
pkg.devEngines?.packageManager?.name === "nub";
if (!declaresNub) {
throw new Error('add "packageManager": "nub@<version>" to package.json before reading as nub');
}
const project = await MANAGERS.nub.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.nub.read({ workspaceRoot: root });
} catch (err) {
if (err instanceof ConvertError && err.type === "package_manager-unexpected") {
project = await getWorkspaceDetails({ root }); // probe all managers instead
} else {
throw err;
}
} Prevention
- Remember nub ignores lockfiles: only the packageManager declaration makes a project nub
- Keep the declaration committed and spelled exactly (name@version regex)
- Use getWorkspaceDetails for discovery; reserve MANAGERS.nub.read for after a positive detect
- Do not edit package.json while migration tooling is running
When it happens
Trigger: Calling MANAGERS.nub.read({ workspaceRoot }) (or having convertProject target a project whose detected manager chain reaches nub.read) when the root package.json declares pnpm/npm/yarn/bun in packageManager/devEngines.packageManager, or declares nothing; also when a concurrent edit removes or changes the packageManager field between detect() and read().
Common situations: Repos with a nub.lock or lock.yaml checked in but no packageManager declaration (detection then falls through to pnpm/yarn/npm based on the lockfile); switching the declaration while tooling is running; copy-pasting read code from the npm/pnpm handlers where lockfile presence would have been enough and assuming nub behaves the same.
Related errors
- package_manager-unexpected
- package_manager-unexpected
- package_manager-unexpected
- bun-workspace_glob_error
- pnpm-workspace_parse_error
AI-assisted analysis of vercel/turborepo@9f94a7d215 (2026-08-16).
Data as JSON: /api/errors/c026d7867051d08a.
Report an issue: GitHub.