vercel/turborepo · error · ConvertError

pnpm-workspace_parse_error

pnpm-workspace_parse_error

Error message

failed to parse ${workspaceFile}

What it means

Thrown by getYamlWorkspaces() in turbo-workspaces when pnpm-workspace.yaml (or aube-workspace.yaml, which shares the code path) exists but yaml.load throws - i.e. the file is syntactically invalid YAML. ConvertError type "pnpm-workspace_parse_error" with the full file path in the message. Subtlety: it is thrown only on YAML syntax errors - a file that parses but has the wrong shape (packages not an array, or missing) silently yields [] and no error. Reached from pnpm/aube read(); detect() passes as soon as the file exists, so the failure appears after detection, mid-read of an otherwise recognized project.

Source

Thrown at packages/turbo-workspaces/src/utils.ts:373

  workspaceFileName
}: {
  workspaceRoot: string;
  workspaceFileName: string;
}): Array<string> {
  const workspaceFile = path.join(workspaceRoot, workspaceFileName);
  if (existsSync(workspaceFile)) {
    try {
      const workspaceConfig = yaml.load(readFileSync(workspaceFile, "utf8"));
      // validate it's the type we expect
      if (
        workspaceConfig instanceof Object &&
        "packages" in workspaceConfig &&
        Array.isArray(workspaceConfig.packages)
      ) {
        return workspaceConfig.packages as Array<string>;
      }
    } catch (err) {
      throw new ConvertError(`failed to parse ${workspaceFile}`, {
        type: "pnpm-workspace_parse_error"
      });
    }
  }

  return [];
}

function expandPaths({
  root,
  lockFile,
  workspaceConfig
}: {
  root: string;
  lockFile: string;
  workspaceConfig?: string;
}) {
  const fromRoot = (p: string) => path.join(root, p);

View on GitHub (pinned to 9f94a7d215)

Solutions

  1. Get the precise line from a YAML parser: `node -e "require('js-yaml').load(require('fs').readFileSync('pnpm-workspace.yaml','utf8'))"` - js-yaml prints position info that the ConvertError deliberately omits
  2. Fix the syntax: use spaces (never tabs) for indentation, keep `packages:` as a YAML list of quoted globs, remove conflict markers and duplicate keys
  3. Verify the shape is `packages:\n - \"apps/*\"` (array of strings) - remember a non-array packages value will not throw but silently disables workspace discovery
  4. Validate the file in CI (parse it with js-yaml and assert Array.isArray(cfg.packages)) before running migrations

Example fix

# before - pnpm-workspace.yaml that throws (alias star, tab indent)
packages: apps/*
	packages:
		- apps/*

# after - valid YAML, quoted globs, space indent
packages:
  - "apps/*"
  - "packages/*"
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync, existsSync } from "node:fs";
import path from "node:path";
import yaml from "js-yaml";

const wsPath = path.join(root, "pnpm-workspace.yaml");
if (existsSync(wsPath)) {
  const cfg = yaml.load(readFileSync(wsPath, "utf8")) as { packages?: unknown };
  if (!Array.isArray(cfg?.packages)) {
    throw new Error(`${wsPath}: 'packages' must be an array of globs`);
  }
}
const details = await getWorkspaceDetails({ root });

Type guard

import { ConvertError } from "turbo-workspaces";

function isPnpmWorkspaceParseError(err: unknown): err is ConvertError {
  return err instanceof ConvertError && err.type === "pnpm-workspace_parse_error";
}

Try / catch

try {
  const details = await getWorkspaceDetails({ root });
} catch (err) {
  if (err instanceof ConvertError && err.type === "pnpm-workspace_parse_error") {
    try {
      yaml.load(readFileSync(path.join(root, "pnpm-workspace.yaml"), "utf8"));
    } catch (e) {
      console.error(`Fix pnpm-workspace.yaml: ${(e as Error).message}`); // includes line numbers
    }
    process.exit(1);
  } else throw err;
}

Prevention

When it happens

Trigger: pnpm-workspace.yaml with tabs used for indentation; an unquoted asterisk scalar such as `packages: apps/*` (YAML reads `*` as an alias reference and js-yaml throws 'unknown alias'); duplicate keys; merge-conflict markers left after a git merge; malformed indent after hand-editing the catalog/workspace list.

Common situations: Copying a workspace glob from docs into the wrong YAML shape (`packages: apps/*` instead of a list under packages); tabs inserted by a configured-for-Tabs editor; merge conflicts in frequently-edited pnpm-workspace.yaml (catalog entries) resolved with markers still present; renames from pnpm-workspace.yml or partial writes after a crashed save.

Understand the failure class

Related errors


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