Summary
openclaw plugins install <scoped-package> fails with ENOENT for any package whose name contains a slash (i.e., all @scope/name npm-style packages on ClawHub). Non-scoped packages install fine.
Reproduction
Output:
Resolving clawhub:@axonflow/[email protected]…
ClawHub code-plugin @axonflow/[email protected] channel=community verification=source-linked
Compatibility: pluginApi=>=2026.3.22 minGateway=2026.3.22
ClawHub package "@axonflow/openclaw" is community; review source and verification before enabling.
ENOENT: no such file or directory, open '/var/folders/.../openclaw-clawhub-package-XXXXXX/@axonflow/openclaw.zip'
For comparison, a non-scoped package installs successfully:
openclaw plugins install mywallet # works
Root cause
In dist/clawhub-CFvPS51z.js (built file, source is presumably similar):
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-clawhub-package-"));
const archivePath = path.join(tmpDir, `${params.name}.zip`);
await fs.writeFile(archivePath, bytes);
When params.name is @axonflow/openclaw, path.join resolves archivePath to <tempdir>/@axonflow/openclaw.zip — i.e., a path containing @axonflow/ as a subdirectory. fs.mkdtemp only creates <tempdir>, not the @axonflow/ child. fs.writeFile does not auto-create parent directories, so it throws ENOENT.
The same bug pattern exists in downloadClawHubSkillArchive for skills (line ~232).
Fix
Either:
// Option A: sanitize the filename
const archivePath = path.join(tmpDir, `${params.name.replace(/\//g, '_')}.zip`);
or:
// Option B: ensure parent dir exists before writing
const archivePath = path.join(tmpDir, `${params.name}.zip`);
await fs.mkdir(path.dirname(archivePath), { recursive: true });
await fs.writeFile(archivePath, bytes);
Option B preserves the existing path layout if anything else relies on it.
Impact
This affects every scoped npm package on ClawHub. Any plugin published as @scope/name is currently uninstallable via openclaw plugins install <name>. The package upload, metadata, and zip artifact are all stored correctly on ClawHub — only the install/download path is broken.
Workaround for users
Until fixed:
npm pack @scope/name
openclaw plugins install ./scope-name-VERSION.tgz
Environment
- OpenClaw: 2026.3.28 (f9b1079)
- macOS, Node 24
- ClawHub CLI: v0.9.0
Summary
openclaw plugins install <scoped-package>fails withENOENTfor any package whose name contains a slash (i.e., all@scope/namenpm-style packages on ClawHub). Non-scoped packages install fine.Reproduction
Output:
For comparison, a non-scoped package installs successfully:
openclaw plugins install mywallet # worksRoot cause
In
dist/clawhub-CFvPS51z.js(built file, source is presumably similar):When
params.nameis@axonflow/openclaw,path.joinresolvesarchivePathto<tempdir>/@axonflow/openclaw.zip— i.e., a path containing@axonflow/as a subdirectory.fs.mkdtemponly creates<tempdir>, not the@axonflow/child.fs.writeFiledoes not auto-create parent directories, so it throws ENOENT.The same bug pattern exists in
downloadClawHubSkillArchivefor skills (line ~232).Fix
Either:
or:
Option B preserves the existing path layout if anything else relies on it.
Impact
This affects every scoped npm package on ClawHub. Any plugin published as
@scope/nameis currently uninstallable viaopenclaw plugins install <name>. The package upload, metadata, and zip artifact are all stored correctly on ClawHub — only the install/download path is broken.Workaround for users
Until fixed:
Environment