pnpm/pnpm · error · PnpmError

SCRIPT_OVERRIDE_IN_WORKSPACE_ROOT

SCRIPT_OVERRIDE_IN_WORKSPACE_ROOT

Error message

The workspace root has a "${typedCommandName}" script, so the built-in "pnpm ${typedCommandName}" command cannot run from a subdirectory

What it means

For built-in commands that a project script can shadow (e.g. `pnpm start`, `pnpm test`), pnpm first checks the manifest of the current directory: if that project defines a same-named script, pnpm re-routes to `pnpm run <name>`. If it does not, but you are in a workspace subdirectory and the workspace root manifest defines that script, pnpm refuses to guess and throws SCRIPT_OVERRIDE_IN_WORKSPACE_ROOT, with a hint to run `pnpm run <cmd>` from the root.

Source

Thrown at pnpm11/pnpm/src/main.ts:242

    const currentDirManifest = config.dir === context.rootProjectManifestDir
      ? context.rootProjectManifest
      : await safeReadProjectManifestOnly(config.dir)
    if (currentDirManifest?.scripts?.[typedCommandName]) {
      // Redirect to "pnpm run <cmd>"
      cmd = 'run'
      cliParams.unshift(typedCommandName)
      fallbackCommandUsed = true
      config.fallbackCommandUsed = true
      config.extraEnv = {
        ...config.extraEnv,
        npm_command: 'run-script',
      }
    } else if (
      workspaceDir &&
      config.dir !== context.rootProjectManifestDir &&
      context.rootProjectManifest?.scripts?.[typedCommandName]
    ) {
      throw new PnpmError(
        'SCRIPT_OVERRIDE_IN_WORKSPACE_ROOT',
        `The workspace root has a "${typedCommandName}" script, ` +
        `so the built-in "pnpm ${typedCommandName}" command cannot run from a subdirectory`,
        {
          hint: `Run "pnpm run ${typedCommandName}" from the workspace root to execute the script`,
        }
      )
    }
  }

  if (
    cmd != null && recursiveByDefaultCommands.has(cmd) &&
    typeof workspaceDir === 'string'
  ) {
    cliOptions['recursive'] = true
    config.recursive = true

    if (!config.recursiveInstall && !config.filter && !config.filterProd) {

View on GitHub (pinned to 6261b7f388)

Solutions

  1. Run it from the workspace root: `pnpm run <cmd>` executes the root script, as the hint says
  2. Define the script in the subproject's own package.json so `pnpm <cmd>` re-routes locally
  3. Select the project explicitly from the root instead of relying on cwd: `pnpm --filter <pkg> run <cmd>`

Example fix

# before -- cwd is packages/foo, root package.json defines a "test" script
pnpm test

# after
cd <workspace-root> && pnpm run test
# or: pnpm --filter foo run test
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs'

const localScripts: Record<string, string> =
  JSON.parse(readFileSync('package.json', 'utf8')).scripts ?? {}

if (overridable.includes(name) && !(name in localScripts) && process.cwd() !== workspaceRoot) {
  // `pnpm <name>` will throw here - go to the root or use pnpm run explicitly
}

Prevention

When it happens

Trigger: In a monorepo, the root package.json has a script named identically to a built-in command (e.g. `"test"`); you run `pnpm test` inside `packages/foo` whose own manifest has no `test` script, so there is nothing to re-route to locally but the root script collides.

Common situations: Monorepos that keep test/build orchestration at the root; behavior change after upgrading pnpm from versions that silently fell back to the root script; developers cd'ed deep into a workspace package.

Related errors


AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17). Data as JSON: /api/errors/0f881cbdfd5c060b. Report an issue: GitHub.