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:
- Missing
-gflag: Without the global flag, npm treats thecwdas a standard project directory rather than the global prefix. - Incorrect
cwdresolution: Settingcwdto `/lib/node_modules` violates npm's expected directory structure for project-based operations. - No
package.jsonpresent: In standard user-prefix layouts (created vianpm 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 Version | Behavior when <cwd>/package.json is absent |
|---|---|
| npm 10.x | Fails 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:
- Creates a new
package.jsonwith no name/version - Generates a
package-lock.jsoncontaining only the packages specified in the install command - Prunes
node_modulesto match exactly what the new lockfile specifies
Failure Sequence Diagram
openclaw configure β Matrix plugin selection
β
MatrixInstaller.install()
β
spawn(’npm’, [‘install’, …], { cwd:
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 }
});
Option B: Isolated Plugin Dependency Directory (Recommended)
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' ' ')
π Related Errors
Directly Related Errors
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.
Contextually Related npm 11+ Changes
- 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.
Related OpenClaw Issues
- 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).