vercel/turborepo · error · Error

New workspace root detected - unexpected 'workspaces' field

Error message

New workspace root detected - unexpected 'workspaces' field in package.json

What it means

`turbo gen --copy` copies an existing package into the monorepo as a new workspace, then runs post-copy guards: the copied package.json must NOT have a `workspaces` field, because that would mean you copied a monorepo root and are trying to nest one workspace system inside another. On failure the error is logged and the copied directory is rolled back (recursively removed).

Source

Thrown at packages/turbo-gen/src/generators/copy.ts:42

  if (opts.copy.type === "external") {
    logger.log();
    logger.warn("Some manual modifications may be required.");
    logger.dimmed(
      `This ${type} may require local dependencies or a different package manager than what is available in this repo`
    );
    await createProject({
      appPath: location.absolute,
      example: opts.copy.source,
      examplePath: opts.examplePath
    });

    try {
      if (fs.existsSync(newPackageJsonPath)) {
        const packageJson = (await fs.readJSON(
          newPackageJsonPath
        )) as PackageJson;
        if (packageJson.workspaces) {
          throw new Error(
            "New workspace root detected - unexpected 'workspaces' field in package.json"
          );
        }
      } else {
        throw new Error("New workspace is missing a package.json file");
      }

      if (fs.existsSync(path.join(location.absolute, "pnpm-workspace.yaml"))) {
        throw new Error(
          "New workspace root detected - unexpected pnpm-workspace.yaml"
        );
      }
    } catch (err) {
      let message = "UNKNOWN_ERROR";
      if (err instanceof Error) {
        message = err.message;
      }
      logger.error(message);

View on GitHub (pinned to 9f94a7d215)

Solutions

  1. Point --copy at the specific workspace package (the sub-directory whose package.json has no `workspaces` field), not the monorepo root
  2. If the source is genuinely a single app with a stale `workspaces` field, delete that field from its package.json and re-run
  3. For true monorepo-to-monorepo merges, copy each workspace package individually with --copy, or move files manually and register them in the root workspaces globs

Example fix

# before
turbo gen --copy ~/src/my-monorepo        # root has workspaces: ["apps/*"]
# after
turbo gen --copy ~/src/my-monorepo/apps/web
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';

function isSinglePackageSource(dir: string): boolean {
  const pkgPath = `${dir}/package.json`;
  if (!fs.existsSync(pkgPath)) return false;
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
  return !pkg.workspaces; // a workspaces field means it is a monorepo root
}

Type guard

function isMonorepoRoot(pkgJson: { workspaces?: unknown }): boolean {
  return Array.isArray(pkgJson.workspaces) || typeof pkgJson.workspaces === 'object';
}

Prevention

When it happens

Trigger: Passing --copy (or opts.copy.source) a directory whose package.json contains a `workspaces` array — i.e. an npm/Yarn workspaces monorepo root rather than a single package.

Common situations: Trying to absorb a standalone app that was itself a monorepo; copying a repository root when the actual app lives in a sub-package; leftover `workspaces` fields in a single-package repo from an earlier monorepo experiment.

Related errors


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