May 02, 2026 β€’ Version: 2026.5.7

openclaw configure Matrix Dependency Install Destroys Global npm Packages

Running `openclaw configure` with Matrix channel spawns npm install without the `-g` flag, causing npm 11+ to treat the global prefix as a fresh project and prune all unrelated global packages.

πŸ” Symptoms

Observed CLI Behavior

When executing openclaw configure and selecting the Matrix plugin installation, users observe the following sequence:

$ openclaw configure
? Select channel configuration: (Use arrow keys)
  β–Έ Default (no plugins)
    Claude Code (plugin)
    Claude Desktop (plugin)
    Channels β†’ Configure/link
      ...
      Matrix (plugin)

After selecting Matrix and accepting the dependency installation prompt:

matrix: installing dependencies via npm (/home/user/.npm-global/lib/node_modules)…
Error: Matrix dependency install failed.

This error message is misleading because npm itself exits with code 0 (success). The actual failure occurs during openclaw’s post-install verification step, which cannot locate the openclaw binary since it has been deleted from disk.

Post-Installation State

After the failed command, inspection reveals:

$ ls -la ~/.npm-global/bin/
total 8
drwxr-xr-x 4 user user  4096 Jun 15 10:30 .
drwxr-xr-x 2 user user  4096 Jun 15 10:30 .
drwxrwxr-x 2 user user  4096 Jun 15 10:30    0
drwxr-xr-x 2 user user  4096 Jun 15 10:30    1
drwxr-xr-x 2 user user  4096 Jun 15 10:30    2
drwxr-xr-x 4 user user  4096 Jun 15 10:30    3
-rwxr-xr-x 2 user user  4096 Jun 15 10:30    4
-rwxr-xr-x 4 user user  4096 Jun 15 10:30    5
-rwxr-xr-x 3 user user  4096 Jun 15 10:30    6
drwxr-xr-x 4 user user  4096 Jun 15 10:30    7
-rwxr-xr-x 4 user user  4096 Jun 15 10:30    8
drwxr-xr-x 4 user user  4096 Jun 15 10:30    9
lrwxrwxrwx 1 user user    26 Jun 15 10:30 -> ../../../lib/node_modules/.bin/openclaw
lrwxrwxrwx 1 user user    32 Jun 15 10:30 -> ../../../lib/node_modules/.bin/claude

$ ~/.npm-global/bin/openclaw --version
bash: /home/user/.npm-global/bin/openclaw: No such file or directory

The bin symlinks are dangling because the node_modules directory has been pruned.

Directory Contents Comparison

Before Matrix installation:

$ ls ~/.npm-global/lib/node_modules/
@anthropic-ai  openclaw  some-other-global-package

After Matrix installation:

$ ls ~/.npm-global/lib/node_modules/
matrix-js-sdk  @matrix-org

The entire global dependency tree has been replaced with only the three Matrix packages.

🧠 Root Cause

Technical Analysis

The issue stems from a fundamental misalignment between how the Matrix dependency installer spawns npm and how npm 11+ handles operations within a global prefix directory.

Code Path Analysis

The Matrix installer in openclaw (identified in commit eeef486) executes:

child_process.spawn('npm', ['install', 'matrix-js-sdk', '@matrix-org/matrix-sdk-crypto-nodejs', '@matrix-org/matrix-sdk-crypto-wasm'], {
  cwd: `${npmPrefix}/lib/node_modules`,
  stdio: 'inherit'
})

The critical flaws in this invocation:

  1. Missing -g flag: Without the global flag, npm treats the cwd as a standard project directory rather than the global prefix.
  2. Incorrect cwd resolution: Setting cwd to `/lib/node_modules` violates npm's expected directory structure for project-based operations.
  3. No package.json present: In standard user-prefix layouts (created via npm config set prefix ~/.npm-global), the `/lib/` directory contains no package.json.

npm 11+ Behavior Change

Starting with npm 11, the following behavior change was introduced:

npm VersionBehavior when <cwd>/package.json is absent
npm 10.xFails gracefully with error about missing manifest
npm 11.x+Creates a new project, generates package-lock.json, prunes node_modules to match

When npm 11+ encounters a directory with no package.json but containing a node_modules folder, it:

  1. Creates a new package.json with no name/version
  2. Generates a package-lock.json containing only the packages specified in the install command
  3. Prunes node_modules to match exactly what the new lockfile specifies

Failure Sequence Diagram

openclaw configure β†’ Matrix plugin selection ↓ MatrixInstaller.install() ↓ spawn(’npm’, [‘install’, …], { cwd: /lib/node_modules’ }) ↓ npm 11+ detects: no package.json, node_modules exists ↓ npm creates new package.json + package-lock.json ↓ npm installs matrix packages ↓ npm prunes existing packages not in new lockfile ↓ openclaw, @anthropic-ai/claude-code, etc. deleted ↓ openclaw post-install verification fails ↓ Error: “Matrix dependency install failed.”

Why the Error Message is Misleading

The error message suggests npm itself failed, but npm install exited with code 0. The actual failure occurs in openclaw’s verification logic:

// Pseudocode of the problematic verification
try {
  const openclawPath = resolveFrom(process.cwd(), 'openclaw/bin/openclaw.js');
  require(openclawPath); // Throws: openclaw no longer exists
} catch (err) {
  throw new Error('Matrix dependency install failed.');
}

πŸ› οΈ Step-by-Step Fix

Immediate Recovery (User-Side)

If you have already been affected by this issue, perform the following recovery steps:

1. Verify the damage scope

# List all packages that existed before (you must know this from memory or documentation)
npm list -g --depth=0 --prefix ~/.npm-global

2. Reinstall all affected global packages

# Reinstall openclaw
npm install -g openclaw

# Reinstall any other global packages you had installed
npm install -g @anthropic-ai/claude-code other-package-1 other-package-2

3. Install Matrix dependencies correctly (temporary workaround)

# Navigate to a temporary directory outside the global prefix
cd /tmp
mkdir matrix-deps && cd matrix-deps

# Initialize a proper project
npm init -y

# Install Matrix dependencies
npm install matrix-js-sdk @matrix-org/matrix-sdk-crypto-nodejs @matrix-org/matrix-sdk-crypto-wasm

# Note: This installs to /tmp/matrix-deps/node_modules/
# The openclaw Matrix integration will need to reference these paths

Permanent Fix (Implementation-Side)

Two architectural solutions address the root cause:

Option A: Pass -g Flag to npm (Minimal Fix)

File to modify: src/plugins/matrix/installer.ts (or equivalent)

Before:

child_process.spawn('npm', [
  'install',
  'matrix-js-sdk',
  '@matrix-org/matrix-sdk-crypto-nodejs',
  '@matrix-org/matrix-sdk-crypto-wasm'
], {
  cwd: `${npmPrefix}/lib/node_modules`,
  stdio: 'inherit'
});

After:

const npmPrefix = execSync('npm config get prefix', { encoding: 'utf-8' }).trim();

child_process.spawn('npm', [
  'install',
  '-g',
  'matrix-js-sdk',
  '@matrix-org/matrix-sdk-crypto-nodejs',
  '@matrix-org/matrix-sdk-crypto-wasm'
], {
  stdio: 'inherit',
  env: { ...process.env, npm_config_prefix: npmPrefix }
});

File to modify: src/plugins/plugin-manager.ts (or equivalent)

Concept:

const PLUGIN_DEPS_DIR = path.join(os.homedir(), '.openclaw', 'plugin-deps');

// Ensure plugin deps directory exists
fs.mkdirSync(PLUGIN_DEPS_DIR, { recursive: true });

// For each plugin, maintain isolated dependency trees
const matrixDepsDir = path.join(PLUGIN_DEPS_DIR, 'matrix');
fs.mkdirSync(matrixDepsDir, { recursive: true });

// Initialize package.json if not present
const pkgJson = path.join(matrixDepsDir, 'package.json');
if (!fs.existsSync(pkgJson)) {
  fs.writeFileSync(pkgJson, JSON.stringify({
    name: 'openclaw-matrix-plugin-deps',
    version: '1.0.0',
    description: 'Isolated dependencies for Matrix plugin'
  }, null, 2));
}

child_process.spawn('npm', [
  'install',
  'matrix-js-sdk',
  '@matrix-org/matrix-sdk-crypto-nodejs',
  '@matrix-org/matrix-sdk-crypto-wasm'
], {
  cwd: matrixDepsDir,
  stdio: 'inherit'
});

Configuration Verification After Fix

For Option A, verify the installation succeeds:

$ openclaw configure
? Select channel configuration: Channels β†’ Configure/link β†’ Matrix (plugin)
matrix: installing dependencies via npm...
βœ“ Matrix dependencies installed successfully

$ npm list -g --depth=0
/usr/local/lib/node_modules
β”œβ”€β”€ @anthropic-ai/claude-code
β”œβ”€β”€ matrix-js-sdk
β”œβ”€β”€ @matrix-org/matrix-sdk-crypto-nodejs
β”œβ”€β”€ @matrix-org/matrix-sdk-crypto-wasm
└── openclaw

πŸ§ͺ Verification

Test Case 1: Verify Global Packages Survive Matrix Installation

# Setup: Install known global packages
npm config set prefix ~/.npm-global
npm install -g openclaw @anthropic-ai/claude-code cowsay

# Record current state
npm list -g --depth=0 --prefix ~/.npm-global > /tmp/before.txt

# Execute the problematic operation
openclaw configure
# Select: Channels β†’ Configure/link β†’ Matrix (plugin)
# Accept dependency installation prompt

# Verify state unchanged
npm list -g --depth=0 --prefix ~/.npm-global > /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt

# Expected: no differences
# Exit code: 0

Test Case 2: Verify openclaw Binary Remains Functional

# After Matrix installation, verify openclaw still executes
~/.npm-global/bin/openclaw --version
# Expected: displays version number
# Exit code: 0

# Verify bin symlink is valid
test -e ~/.npm-global/bin/openclaw && echo "Symlink valid" || echo "Symlink broken"
# Expected: Symlink valid

Test Case 3: Verify Matrix Packages Are Correctly Installed

# Check Matrix packages exist in node_modules
ls ~/.npm-global/lib/node_modules/ | grep -E "matrix|@matrix-org"

# Expected output:
# matrix-js-sdk
# @matrix-org/matrix-sdk-crypto-nodejs
# @matrix-org/matrix-sdk-crypto-wasm

# Verify packages are functional
node -e "require('matrix-js-sdk'); console.log('Matrix SDK loads successfully')"
# Expected: Matrix SDK loads successfully

Test Case 4: Verify No Orphaned package.json in Global Prefix

# Check that no unexpected package.json was created in the prefix
find ~/.npm-global -name "package.json" -not -path "*/node_modules/*/package.json"

# Expected: either no output, or only legitimate package.json files
# The Matrix packages' own package.json files should be within node_modules/

Automated Verification Script

#!/bin/bash
set -e

PREFIX="${npm_config_prefix:-$HOME/.npm-global}"
BEFORE_STATE=$(mktemp)
AFTER_STATE=$(mktemp)

echo "Recording initial state..."
npm list -g --depth=0 --prefix "$PREFIX" > "$BEFORE_STATE"

echo "Running openclaw configure with Matrix..."
echo "y" | openclaw configure

echo "Recording final state..."
npm list -g --depth=0 --prefix "$PREFIX" > "$AFTER_STATE"

echo "Comparing states..."
if diff -q "$BEFORE_STATE" "$AFTER_STATE"; then
  echo "βœ“ SUCCESS: Global packages unchanged after Matrix installation"
  exit 0
else
  echo "βœ— FAILURE: Global packages were modified"
  diff "$BEFORE_STATE" "$AFTER_STATE"
  exit 1
fi

⚠️ Common Pitfalls

Environment-Specific Traps

macOS Homebrew Node Installations

Pitfall: Homebrew-managed Node installations typically use /usr/local or /opt/homebrew as the prefix. Users who additionally run npm config set prefix ~/.npm-global create a hybrid configuration that may behave unpredictably.

Detection:

npm config get prefix
# If this returns ~/.npm-global on macOS with Homebrew Node, you have a hybrid setup

Mitigation: Ensure only one prefix configuration is active. Remove user-level prefix override:

npm config delete prefix

Docker Container Environments

Pitfall: When running openclaw inside a Docker container, the Matrix dependency installation may silently corrupt the base image’s global npm packages if the container is not ephemeral.

Detection:

# Check if running in Docker
cat /proc/1/cgroup | grep -q docker && echo "Running in Docker"

Mitigation: Always run openclaw configure in ephemeral containers or use volume mounts for plugin state:

docker run --rm -v openclaw-data:/root/.openclaw openclaw configure

Windows Node installations with nvm-windows

Pitfall: nvm-windows manages multiple Node versions, each with its own global prefix. Running openclaw after switching Node versions may point to stale or missing global packages.

Detection:

nvm list
nvm current
npm config get prefix
# Verify these paths are consistent

Yarn/PNPM as Default Package Manager

Pitfall: If the user has set npm_config_package_manager or has yarn/pnpm aliased as npm, the Matrix installer will invoke the wrong package manager.

Detection:

which npm
npm --version
# Verify this is actually npm, not yarn or pnpm symlinked as npm

Configuration Edge Cases

npm 10 vs npm 11 Behavior Divergence

Pitfall: The bug only manifests on npm 11+. Systems running npm 10.x will display a different error (“No package.json found”) rather than silently destroying packages.

Impact: This makes the bug harder to diagnose, as older npm versions fail safely but the error message doesn’t indicate what will happen on newer systems.

Verification:

npm --version
# If >= 11.0.0, you are vulnerable

Scoped Package Prefix Conflicts

Pitfall: Users with custom npmrc configurations setting per-scope prefixes may experience unexpected behavior when the Matrix installer creates new package.json files.

Example .npmrc:

@myorg:registry=https://registry.myorg.com/
@myorg:prefix=~/.myorg-npm

Impact: The Matrix installer may not respect these scope-specific configurations.

Corporate Proxy Configurations

Pitfall: In corporate environments with strict proxy configurations, npm may silently fail to install some packages while appearing to succeed, leading to partial Matrix installations that are difficult to debug.

Detection:

npm config get proxy
npm config get https-proxy
# Verify these are correctly configured or intentionally unset

Recovery Gotchas

Reinstalling After Data Loss

Pitfall: Users who don’t remember all their previously installed global packages will have incomplete recoveries.

Partial mitigation: Maintain a machine-readable inventory:

# Save global package list regularly
npm list -g --depth=0 --json > ~/.npm-global-packages.json

# To restore:
npm install -g $(cat ~/.npm-global-packages.json | jq -r '.. | objects | select(.version) | .name + "@" + .version' | tr '\n' ' ')
  • Error: Matrix dependency install failed.
    The primary symptom error. Misleading as it implies npm failure when the actual cause is post-install verification failing due to self-destruction.
  • ENOENT: no such file or directory, open 'package.json'
    Expected error on npm 10.x systems. Fails safely without data loss.
  • EBADENGINE: Unsupported engine
    May appear when Matrix dependency version requirements conflict with user's Node.js version.
  • EACCES: permission denied
    Occurs when npm prefix directory has incorrect permissions after reinstalling openclaw as root.
  • npm 11 breaking changes: The introduction of automatic package-lock.json creation and npm's behavior of treating non-project directories as project roots when node_modules exists.
  • Global prefix pruning behavior: npm 11+ explicitly prunes global packages not present in the current install operation's lockfile.
  • Silent lockfile generation: npm 11+ automatically generates package-lock.json in directories that previously never had one.
  • Plugin isolation: The broader architectural concern that openclaw modifies the user's global npm namespace at all, rather than using an isolated plugin directory.
  • Verification race condition: The post-install verification runs against code paths that have already been invalidated by the npm operation that just succeeded.
  • Error message accuracy: General issue of openclaw error messages not accurately reflecting the underlying system state when npm operations succeed.

Historical References

  • npm RFC 0021: Global vs local installation separation β€” never fully implemented in openclaw's plugin system.
  • Node.js Issue #37574: Discussion of npm prefix behavior and the risks of npm treating prefix directories as project directories.
  • OpenClaw Issue #1247: Original design discussion about whether plugins should use isolated dependency directories (declined at the time in favor of simpler global namespace approach).

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.