ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY: your coding agent's pnpm 11 is not your pnpm 10

·4 mins

A coding agent ran pnpm test in my repo and got this:

Scope: all 6 workspace projects
[ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY] Aborted removal of modules directory due to no TTY

If you are running pnpm in CI, set the CI environment variable to "true", or set "confirmModulesPurge" to "false".

The stack trace ended in runDepsStatusCheck (.../node_modules/.pnpm/pnpm@11.7.0/...). My package.json says "packageManager": "pnpm@10.33.0", and pnpm -v in my terminal prints 10.33.0.

If you see this error only inside an agent, the agent is running its own pnpm, and following the hint makes things worse.

The setup

  • macOS, pnpm 10.33.0 through Corepack, a pnpm monorepo with a Next.js 15 app
  • The Codex desktop app, which ships its own Node 24 and pnpm under ~/.cache/codex-runtimes/codex-primary-runtime/dependencies/: pnpm 11.7.0 in July, 11.25.0 in October
  • The agent runs pnpm test without a TTY

What went wrong

The agent followed the hint and reran with CI=true pnpm test. pnpm stopped asking and did what it had wanted to do from the start:

Recreating /Users/me/project/node_modules
Progress: resolved 378, reused 0, downloaded 137, added 12
...
[WARN] GET https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.19.tgz error (ERR_PNPM_ENOSPC). Will retry in 10 seconds. 2 retries left.
[WARN] GET https://registry.npmjs.org/next/-/next-15.5.19.tgz error (ERR_PNPM_ENOSPC). Will retry in 10 seconds. 2 retries left.
...
Progress: resolved 378, reused 0, downloaded 377, added 377, done

[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: cpu-features@0.0.10, esbuild@0.21.5, esbuild@0.28.1, protobufjs@7.6.4, sharp@0.34.5, ssh2@1.17.0

It deleted node_modules and downloaded every package again (reused 0) into a second store, and the disk ran out on the way. It left an untracked .pnpm-store/ in the repo and wrote placeholders into the tracked pnpm-workspace.yaml:

allowBuilds:
  cpu-features: set this to true or false
  esbuild: set this to true or false
  protobufjs: set this to true or false
  sharp: set this to true or false
  ssh2: set this to true or false

No tests ran. After that, even pnpm exec tsc --noEmit failed with the same NO_TTY error, so the agent switched to ./node_modules/.bin/tsc and ./node_modules/.bin/vitest, reverted the YAML, deleted .pnpm-store/ and committed with only the focused tests green.

Ten weeks later, next build failed:

 ⚠ Attempted to load @next/swc-darwin-arm64, but it was not installed
 ⨯ Failed to load SWC binary for darwin/arm64, see more info here: https://nextjs.org/docs/messages/failed-loading-swc

The July install had ended one package short (378 resolved, 377 added), and the SWC binary was one of the two downloads that hit ENOSPC. A later agent session “fixed” the build by installing the binary into /tmp and symlinking it into node_modules, a fix that lasts until macOS cleans /tmp.

Fix

First, ask the agent’s shell which pnpm it gets:

command -v pnpm
pnpm -v

Then make the agent use your version. Corepack reads packageManager, whatever is first on PATH:

corepack pnpm test   # runs pnpm 10.33.0

Write that into AGENTS.md (or your agent’s equivalent), along with one rule: if pnpm wants to remove node_modules, stop and report.

To make the bundled pnpm harmless as well, turn off the automatic install in pnpm-workspace.yaml:

verifyDepsBeforeRun: false

That is the pnpm 10 default, so nothing changes for you. I checked it with the bundled pnpm 11.25.0: with the setting, it runs the script; without it, it installs first.

If you already followed the hint, clean up with your own pnpm:

git checkout -- pnpm-workspace.yaml
rm -rf .pnpm-store
corepack pnpm install --frozen-lockfile

Run that in a terminal. If pnpm asks to remove node_modules, say yes; this time it rebuilds against your own store.

Why

Three pnpm 11 changes line up:

  1. verify-deps-before-run defaults to install (pnpm 10: false). pnpm test and pnpm exec check node_modules first and install if anything is off.
  2. pnpm 11 uses store v11; pnpm 10 uses v10. node_modules/.modules.yaml records the store it was linked from, so to pnpm 11 a working pnpm 10 install looks incompatible and has to be purged. confirmModulesPurge defaults to true; a purge without a TTY aborts, and CI=true drops the confirmation but not the purge.
  3. Ignored build scripts are an error, and pnpm writes the allowBuilds placeholders for you to fill in.

On my Mac, the two stores now sit side by side in ~/Library/pnpm/store/: 851 MB in v10, 588 MB in v11.

Why didn’t packageManager catch it? pnpm 11 does honor it: run the bundled binary directly and pnpm -v prints 10.33.0, because it switches to the pinned version. The wrapper in the October runtime turns that off:

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export pnpm_config_pm_on_fail=ignore
exec "${SCRIPT_DIR}/../../node/bin/node" "${SCRIPT_DIR}/../../node/node_modules/pnpm/bin/pnpm.mjs" "$@"

The environment variable beats anything in the repo. With pmOnFail: error in pnpm-workspace.yaml, or devEngines.packageManager with "onFail": "error", the wrapper still runs 11.25.0.

Takeaway

An agent runtime can bring its own toolchain and switch off the check that would have caught it. When an agent reports “tests blocked by the environment”, find out which binary it ran before you believe the report.

Run pnpm -v in your agent’s shell today and compare it with packageManager.