vercel/turborepo · error · GeneratorError

Unable to load generators

Error message

Unable to load generators

What it means

runCustomGenerator bundles your generator config with esbuild and loads it through nodePlop. If nodePlop throws while loading (the original error is printed to stderr first, then createPlopFromConfig returns undefined), turbo-gen throws GeneratorError 'Unable to load generators' (type plop_unable_to_load_config). The config exists but cannot be loaded as a plop config.

Source

Thrown at packages/turbo-gen/src/utils/plop.ts:357

export async function runCustomGenerator({
  project,
  generator,
  bypassArgs,
  configPath
}: {
  project: Project;
  generator: Generator;
  bypassArgs?: Array<string>;
  configPath?: string;
}): Promise<void> {
  const resolvedConfigPath = configPath ?? generator.configPath;
  const destBasePath = configPath ?? generator.destBasePath;

  const plop = await createPlopFromConfig(resolvedConfigPath, destBasePath);

  if (!plop) {
    throw new GeneratorError("Unable to load generators", {
      type: "plop_unable_to_load_config"
    });
  }

  let gen: PlopGenerator | undefined;
  try {
    gen = plop.getGenerator(generator.name) as PlopGenerator | undefined;
  } catch {
    // plop throws when generator not found
  }

  if (!gen) {
    throw new GeneratorError(
      `Generator ${qualifiedName(generator)} not found`,
      { type: "plop_generator_not_found" }
    );
  }

View on GitHub (pinned to 9f94a7d215)

Solutions

  1. Look at the line printed immediately before this error — the real load exception is logged there by createPlopFromConfig
  2. Run the config standalone to reproduce: `npx tsx turbo/generators/config.ts`
  3. Make the config default-export a function: `export default function (plop) { plop.setGenerator(...) }`
  4. Install any packages the config imports (only `@inquirer/prompts` is provided by the CLI without installing)

Example fix

// turbo/generators/config.ts (before)
export const config = (plop: NodePlopAPI) => { plop.setGenerator(...) };
// after
export default function (plop: NodePlopAPI) { plop.setGenerator(...) }
Defensive patterns

Strategy: try-catch

Validate before calling

import path from 'node:path';
import { build } from 'esbuild';

async function configLoads(configPath: string): Promise<boolean> {
  try {
    await build({ entryPoints: [configPath], bundle: true, format: 'cjs', platform: 'node', write: false, logLevel: 'silent' });
    return true;
  } catch {
    return false;
  }
}

Try / catch

try {
  await runCustomGenerator({ project, generator, configPath });
} catch (err) {
  if (err instanceof GeneratorError && err.type === 'plop_unable_to_load_config') {
    // the real load error was printed to stderr just before this throw — inspect it,
    // then surface a targeted message about default-export shape or missing deps
    throw new Error(`generator config failed to load — check default export and imports`);
  }
  throw err;
}

Prevention

When it happens

Trigger: The config file throws at module top level when executed; it does not default-export a plop config function `export default function (plop) {...}`; it imports a dependency that cannot be resolved from your project (only @inquirer/prompts is CLI-provided).

Common situations: TS configs importing packages not installed in that workspace; exporting a const or named export instead of a default function; refactors introducing a runtime error; CJS/ESM confusion where module.exports ends up double-wrapped.

Related errors


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