< Back to all hacks

#38 --legacy-peer-deps

Dirty COW
Problem
npm dependency version conflicts during OpenClaw install.
Solution
--legacy-peer-deps flag alongside --ignore-scripts to bypass peer dependency conflicts.
Lesson
npm v7+ strict peer dependency resolution breaks installs for large projects with conflicting transitive dependencies.

Context

Starting with npm v7, peer dependencies are installed automatically and version conflicts cause hard errors instead of warnings. OpenClaw's dependency tree includes multiple packages that declare conflicting peer dependency requirements. For example, one package may require typescript@^4.0.0 while another requires typescript@^5.0.0, or two packages may need different major versions of a shared transitive dependency like @types/node.

On the Moto E2, a failed npm install is expensive: it consumes time, disk I/O, and temporary space on the constrained 4 GB internal storage. The phone can't afford to retry installs after peer dependency resolution failures, and manually resolving each conflict by pinning versions is impractical for a project with hundreds of transitive dependencies.

The --legacy-peer-deps flag tells npm to use the npm v3-v6 algorithm for peer dependency resolution: install whatever is declared, ignore version conflicts, and let runtime determine if things actually break. Combined with --ignore-scripts (Hack #5) to skip native compilation, this gives a reliable two-flag install recipe.

Implementation

The standard install command for OpenClaw on the Moto E2:

# Full install command with both flags
npm install -g openclaw --ignore-scripts --legacy-peer-deps

Breaking down what each flag does:

# --ignore-scripts: skip pre/post install scripts (native compilation)
# --legacy-peer-deps: use npm v3-v6 peer dep resolution (permissive)

# Without --legacy-peer-deps, npm v7+ exits with:
# npm ERR! Could not resolve dependency:
# npm ERR! peer typescript@"^4.9.5" from @openclaw/core@1.2.3
# npm ERR! node_modules/@openclaw/core
# npm ERR!   @openclaw/core@"*" from openclaw@2.0.0
# npm ERR!
# npm ERR! Fix the upstream dependency conflict, or retry
# npm ERR! this command with --force, or --legacy-peer-deps

For reinstalls or updates, the same flags are needed every time:

# Clean reinstall
rm -rf $PREFIX/lib/node_modules/openclaw
npm install -g openclaw --ignore-scripts --legacy-peer-deps

# Update to a new version
npm update -g openclaw --ignore-scripts --legacy-peer-deps

To avoid forgetting the flags, you can set them as npm defaults:

# Make both flags permanent (written to ~/.npmrc)
npm config set legacy-peer-deps true
npm config set ignore-scripts true

# Verify settings
npm config list
# Expected: legacy-peer-deps = true, ignore-scripts = true

# Now a plain install works:
npm install -g openclaw

However, making ignore-scripts a global default means ALL npm installs skip lifecycle scripts, which may break other packages that legitimately need them. A safer approach is to only persist legacy-peer-deps:

# Only persist the peer deps flag
npm config set legacy-peer-deps true

# Still pass --ignore-scripts explicitly for OpenClaw
npm install -g openclaw --ignore-scripts

Verification

# Verify OpenClaw installed successfully:
ls $PREFIX/lib/node_modules/openclaw/dist/cli.js
# Expected: file exists

# Check for peer dependency warnings in install output:
npm install -g openclaw --ignore-scripts --legacy-peer-deps 2>&1 | grep "WARN"
# Expected: warnings but no errors (ERESOLVE gone)

# Verify the gateway starts correctly despite peer dep mismatches:
node22-icu --max-old-space-size=112 $PREFIX/lib/node_modules/openclaw/dist/cli.js gateway run --port 9000 &
sleep 10
curl -s http://localhost:9000/api/status
# Expected: JSON response confirming gateway is running

# Check npm config for persistence:
npm config get legacy-peer-deps
# Expected: true (if set permanently)

Gotchas

  • --legacy-peer-deps does NOT fix actual runtime incompatibilities. If two packages truly require different versions of a shared dependency at runtime, you'll get errors when that code path executes. For OpenClaw on PocketClaw, no runtime peer dep conflicts have been observed because the conflicting packages are either stubbed (Hack #15) or never loaded (lazy loading, Hack #18)
  • --force is an alternative to --legacy-peer-deps but is more aggressive: it ignores ALL dependency checks, not just peer deps. Prefer --legacy-peer-deps as the more targeted fix
  • On Node.js v12 (Termux native), npm is v6 and peer deps are already lenient by default. This flag is only needed with the Node.js v22 setup where npm v10+ enforces strict resolution
  • If you set legacy-peer-deps in .npmrc globally and later switch to a different project that needs strict peer dep checking, you'll need to override it with --no-legacy-peer-deps
  • The flag must be used on every npm install and npm update unless persisted via npm config set. Forgetting it on an update will trigger the same ERESOLVE errors

Result

MetricWithout FlagWith Flag
npm install resultERESOLVE error (exit 1)Success
Peer dep resolutionStrict (npm v7+)Permissive (npm v3-v6)
Runtime impactN/A (install fails)None observed
Install timeFails immediately~3 minutes
Combined with --ignore-scriptsStill fails on peer depsReliable two-flag recipe