vercel/turborepo · warning

Cannot infer turbo version due to use of `catalog` protocol.

Error message

Cannot infer turbo version due to use of `catalog` protocol. Remove `turbo` from your PNPM catalog to ensure correct turbo version is used

What it means

turbo-ignore tailors its affected-check to the workspace's turbo version and first tries to infer it from `dependencies.turbo` / `devDependencies.turbo` in package.json. A `catalog:` specifier (the pnpm catalog protocol) cannot be resolved to a concrete version from that file alone, so turbo-ignore warns, abandons this inference source, and falls through to the next ones (turbo.json's `turbo` field, then defaults).

Source

Thrown at packages/turbo-ignore/src/get-turbo-version.ts:30

  let { turboVersion } = args;
  if (turboVersion) {
    info(`Using turbo version "${turboVersion}" from arguments`);
    return turboVersion;
  }

  const packageJsonPath = path.join(root, "package.json");
  try {
    const raw = fs.readFileSync(packageJsonPath, "utf8");
    const packageJson = JSON.parse(raw) as PackageJson;
    const dependencies = packageJson.dependencies?.turbo;
    const devDependencies = packageJson.devDependencies?.turbo;
    turboVersion = dependencies || devDependencies;
    if (turboVersion !== undefined) {
      if (!turboVersion.startsWith("catalog:")) {
        info(`Inferred turbo version "${turboVersion}" from "package.json"`);
        return turboVersion;
      }
      warn(
        "Cannot infer turbo version due to use of `catalog` protocol. Remove `turbo` from your PNPM catalog to ensure correct turbo version is used"
      );
    }
  } catch (e) {
    error(
      `"${packageJsonPath}" could not be read. turbo-ignore turbo version inference failed`
    );
    return null;
  }

  const turboJSONPath = path.join(root, "turbo.json");
  try {
    const rawTurboJson = fs.readFileSync(turboJSONPath, "utf8");
    const turboJson: { tasks?: unknown; pipeline?: unknown } =
      JSON5Parse(rawTurboJson);
    if ("tasks" in turboJson) {
      info(`Inferred turbo version ^2 based on "tasks" in "turbo.json"`);
      return "^2";

View on GitHub (pinned to f9245100cf)

Solutions

  1. Replace the catalog reference for turbo with an explicit version range in that package.json (as the warning instructs)
  2. Ensure turbo.json declares its `"turbo"` version so the fallback inference finds a real value
  3. Migrate the check from turbo-ignore to `turbo query affected`, since turbo-ignore is deprecated anyway

Example fix

// before: package.json
"devDependencies": { "turbo": "catalog:" }

// after: package.json
"devDependencies": { "turbo": "^2.3.3" }
Defensive patterns

Strategy: type-guard

Validate before calling

import fs from 'node:fs';

const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const spec = pkg.dependencies?.turbo ?? pkg.devDependencies?.turbo;
if (typeof spec === 'string' && spec.startsWith('catalog:')) {
  throw new Error('turbo-ignore cannot resolve catalog: specifiers — pin a version');
}

Type guard

type CatalogSpecifier = `catalog:${string}`;

function isCatalogSpecifier(spec: string): spec is CatalogSpecifier {
  return spec.startsWith('catalog:');
}

Prevention

When it happens

Trigger: package.json contains `"turbo": "catalog:"` or `catalog:<name>` under dependencies/devDependencies, and turbo-ignore runs for that directory (VERCEL build step or CI script), hitting the startsWith('catalog:') branch.

Common situations: Monorepos that adopted pnpm catalogs for all internal dependency versions; turbo-ignore then runs with a default/fallback version instead of the cataloged one, potentially mismatching behavior.

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/c075d5f881f8eacf. Report an issue: GitHub.