mastra-ai/mastra · error · Error

Plugin at ${pluginRoot} must declare an exact pnpm version i

Error message

Plugin at ${pluginRoot} must declare an exact pnpm version in package.json using "packageManager": "pnpm@x.y.z".

What it means

getPnpmVersion requires the plugin's package.json to pin an exact pnpm version via the `packageManager` field. This throw fires when the field is missing or is not a string. The exact version is needed so dependency installation is reproducible across machines.

Source

Thrown at mastracode/sdk/src/plugins/dependencies.ts:104

  while (isInsideDirectory(current, root)) {
    if (fs.existsSync(path.join(current, 'package.json'))) {
      return current;
    }
    if (current === root) break;
    current = path.dirname(current);
  }

  return undefined;
}

function isInsideDirectory(targetPath: string, root: string): boolean {
  const resolvedTarget = path.resolve(targetPath);
  const resolvedRoot = path.resolve(root);
  return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);
}

function getPnpmVersion(pluginRoot: string, packageManager: unknown): string {
  if (typeof packageManager !== 'string') {
    throw new Error(
      `Plugin at ${pluginRoot} must declare an exact pnpm version in package.json using "packageManager": "pnpm@x.y.z".`,
    );
  }

  const match = /^pnpm@(\d+\.\d+\.\d+)$/.exec(packageManager);
  if (!match?.[1]) {
    throw new Error(
      `Plugin at ${pluginRoot} must declare an exact pnpm version in package.json using "packageManager": "pnpm@x.y.z".`,
    );
  }
  return match[1];
}

function getInstallCommand(pluginRoot: string, pnpmVersion: string): InstallCommand {
  const installArgs = ['install', '--ignore-workspace'];
  if (hasFile(pluginRoot, 'pnpm-lock.yaml')) installArgs.push('--frozen-lockfile');
  installArgs.push('--ignore-scripts');

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Add `"packageManager": "pnpm@<exact-version>"` (e.g. `pnpm@9.12.0`) to the plugin's package.json.
  2. Run `corepack use pnpm@latest` in the plugin directory to set the field automatically.
  3. Commit the updated package.json and reinstall the plugin.

Example fix

// before: plugin package.json
{
  "name": "my-plugin"
}

// after
{
  "name": "my-plugin",
  "packageManager": "pnpm@9.12.0"
}
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
export function assertPinnedPnpm(pluginRoot: string): void {
  const pkg = JSON.parse(fs.readFileSync(`${pluginRoot}/package.json`, 'utf8'));
  const pm = pkg.packageManager;
  if (typeof pm !== 'string' || !/^pnpm@\d+\.\d+\.\d+$/.test(pm)) {
    throw new Error(`${pluginRoot}: add "packageManager": "pnpm@x.y.z" to package.json`);
  }
}

Type guard

function isPinnedPnpm(v: unknown): v is `pnpm@${number}.${number}.${number}` {
  return typeof v === 'string' && /^pnpm@\d+\.\d+\.\d+$/.test(v);
}

Prevention

When it happens

Trigger: Calling pnpmVersion (via getPnpmVersion) for a plugin whose package.json either has no `packageManager` key or whose value is not a string (e.g. null after JSON parse, or a non-string value).

Common situations: Plugin authors forgot to add `packageManager`; the field was stripped by a publish step; hand-edited package.json with a malformed value.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/840bf8f3429243f3. Report an issue: GitHub.