Skip to content

v2026.2.26: bun global install fails with 'unsafe plugin manifest path' (hardlink rejection) #28404

@puneet1409

Description

@puneet1409

Description

After upgrading to v2026.2.26 via bun install -g openclaw@latest, all CLI commands fail with config validation errors for every bundled extension plugin:

Invalid config at ~/.openclaw/openclaw.json:
- plugins: plugin: unsafe plugin manifest path: ~/.bun/install/global/node_modules/openclaw/extensions/telegram/openclaw.plugin.json (validation)
- plugins: plugin: unsafe plugin manifest path: ~/.bun/install/global/node_modules/openclaw/extensions/googlechat/openclaw.plugin.json (validation)
... (37 bundled extensions, all rejected)

The gateway process itself starts but plugins fail to load, so channels (Telegram, Google Chat, etc.) don't work. Downgrading to v2026.2.23 resolves the issue.

Root Cause

This is a different variant from #28122 and #28175 (which are about pnpm's symlink layout).

Bun's package manager uses hardlinks from its content-addressable cache to the install directory. Every file in ~/.bun/install/global/node_modules/openclaw/ has nlink: 2 (one link in the bun cache, one in the install dir):

$ stat ~/.bun/install/global/node_modules/openclaw/extensions/telegram/openclaw.plugin.json
Links: 2   # hardlinked from bun cache

v2026.2.26's openVerifiedFileSync (in openclaw-root-*.js) rejects files with nlink > 1:

// Line ~485 of openclaw-root-*.js
if (params.rejectHardlinks && preOpenStat.nlink > 1) return {
    ok: false,
    reason: "validation"
};

This is called from openBoundaryFileSync with rejectHardlinks: params.rejectHardlinks ?? true (defaults to true).

Since bun hardlinks ALL files (~10,000+ files in the package), every plugin manifest AND entry file is rejected.

No symlinks are involved — readlink -f confirms all paths are direct:

$ readlink -f ~/.bun/install/global/node_modules/openclaw
/home/ubuntu/.bun/install/global/node_modules/openclaw  # not a symlink

Workaround

Break the hardlinks after every bun install -g openclaw:

PKG_DIR="$HOME/.bun/install/global/node_modules/openclaw"
find "$PKG_DIR" -type f -links +1 -exec sh -c \
  'for f; do cp "$f" "$f.tmp" && mv "$f.tmp" "$f"; done' _ {} +

This copies each file in-place, creating a new inode with nlink: 1.

Suggested Fix

For bundled plugins (shipped inside the openclaw package itself), the hardlink check should be relaxed. Options:

  1. Set rejectHardlinks: false when loading bundled plugin manifests (origin === "bundled")
  2. Only enforce rejectHardlinks for user-installed/workspace plugins where TOCTOU via hardlinks is an actual attack vector
  3. Skip the nlink check entirely for files inside the openclaw package directory

The security intent of rejectHardlinks (preventing TOCTOU attacks via hardlinked files) doesn't apply to bundled plugins — these are package manager artifacts, not attacker-controlled.

Environment

  • OpenClaw version: 2026.2.26 (works on 2026.2.23)
  • Package manager: bun 1.3.8
  • OS: Ubuntu 24.04 LTS (amd64), GCP e2-small
  • Install method: bun install -g openclaw@latest
  • Node.js: v22.x

Related Issues

This issue and the pnpm issues share the same underlying problem: v2026.2.26's path security hardening doesn't account for how package managers (bun → hardlinks, pnpm → symlinks) store files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions