{"record":{"id":"c06a4429cfcce9b7","repo":"vercel/turborepo","slug":"new-workspace-root-detected-unexpected-pnpm-work","errorCode":null,"errorMessage":"New workspace root detected - unexpected pnpm-workspace.yaml","messagePattern":"New workspace root detected - unexpected pnpm-workspace\\.yaml","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/turbo-gen/src/generators/copy.ts","lineNumber":51,"sourceCode":"      examplePath: opts.examplePath\n    });\n\n    try {\n      if (fs.existsSync(newPackageJsonPath)) {\n        const packageJson = (await fs.readJSON(\n          newPackageJsonPath\n        )) as PackageJson;\n        if (packageJson.workspaces) {\n          throw new Error(\n            \"New workspace root detected - unexpected 'workspaces' field in package.json\"\n          );\n        }\n      } else {\n        throw new Error(\"New workspace is missing a package.json file\");\n      }\n\n      if (fs.existsSync(path.join(location.absolute, \"pnpm-workspace.yaml\"))) {\n        throw new Error(\n          \"New workspace root detected - unexpected pnpm-workspace.yaml\"\n        );\n      }\n    } catch (err) {\n      let message = \"UNKNOWN_ERROR\";\n      if (err instanceof Error) {\n        message = err.message;\n      }\n      logger.error(message);\n\n      // rollback changes\n      await fs.rm(location.absolute, { recursive: true, force: true });\n      return;\n    }\n  } else if (source) {\n    const filterFunc: CopyFilterAsync = async (src) =>\n      !src.includes(\"node_modules\");\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/packages/turbo-gen/src/generators/copy.ts#L33-L69","documentation":"The turbo-gen copy generator rejects copy sources that are themselves monorepo roots. Besides the workspaces field, it treats a pnpm-workspace.yaml at the destination root as proof of a nested monorepo. It throws this Error when that file exists after the copy, then logs it and deletes the copied directory.","triggerScenarios":"The external example copied into your monorepo contains a pnpm-workspace.yaml at its root. The existsSync check after the package.json validation finds the file and throws. The generator rolls back the destination directory.","commonSituations":"Copying a pnpm-based monorepo example instead of one of its packages. An example that ships a pnpm-workspace.yaml for tooling reasons even though it is a single package.","solutions":["Copy a single package from inside the pnpm monorepo by setting the example path to that package directory","If you own the source and it is truly a single package, delete or rename the pnpm-workspace.yaml and commit a workspace.yml-free variant","Pick a different example that is not a workspace root"],"exampleFix":"# before: copy a pnpm monorepo root\nnpm exec turbo gen -- --copy https://github.com/org/pnpm-monorepo\n# after: copy one workspace from it\nnpm exec turbo gen -- --copy https://github.com/org/pnpm-monorepo --example-path packages/ui","handlingStrategy":"validation","validationCode":"import fs from \"node:fs\";\n\n// reject copy sources that carry pnpm workspace manifests\nif (fs.existsSync(path.join(sourceDir, \"pnpm-workspace.yaml\"))) {\n  throw new Error(\"Copy source is a pnpm monorepo root; choose one of its packages\");\n}","typeGuard":"function isPnpmWorkspaceRoot(dir: string): boolean {\n  return fs.existsSync(`${dir}/pnpm-workspace.yaml`);\n}","tryCatchPattern":null,"preventionTips":["Check for pnpm-workspace.yaml when vetting external examples","Copy a single workspace from monorepo examples via the example path","Keep single-package examples free of workspace manifest files"],"tags":["turbo-gen","generator","copy","monorepo","pnpm","validation"],"backgroundTag":"template-validation-failed","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}