openclaw - ✅(Solved) Fix Scoped npm packages from ClawHub fail to install with ENOENT [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#66618Fetched 2026-04-15 06:25:20
View on GitHub
Comments
1
Participants
1
Timeline
6
Reactions
0
Participants
Timeline (top)
cross-referenced ×2referenced ×2closed ×1commented ×1

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.

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 Action

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.

PR fix notes

PR #40: release(v1.2.3): exclude tests from ClawHub archive

Description (problem / solution / changelog)

Summary

Ships v1.2.3 to unblock `openclaw plugins install @axonflow/openclaw` on the just-released OpenClaw 2026.4.14, where our unit tests were triggering the install-time scanner.

Background

OpenClaw 2026.4.14 (released April 15 UTC) landed two relevant changes:

  1. Fixed scoped-package ENOENT bug (openclaw/openclaw#66618) — the bug we filed yesterday. Users on 2026.4.14+ can now use the primary install command without the `npm pack` workaround.
  2. Install scanner upgraded from warn → block on files that combine `process.env.X` access with `fetch()` calls. Our `tests/telemetry.test.ts` mocks both for opt-out coverage, which the scanner treats as "possible credential harvesting" and blocks installation. Filed upstream: openclaw/openclaw#66840.

Change

New `.clawhubignore` (gitignore-syntax, natively supported by `clawhub package publish`) excludes: `tests/`, `tests/`, `.{test,spec}.{ts,js}`, `src/`, `scripts/`, `.github/`, `tsconfig.json`, `jest.config.`, `.eslintrc`, `.prettierrc*`, `coverage/`, `.tgz`, `dist/**/.map`.

Runtime artifacts that still ship to ClawHub: `dist/`, `openclaw.plugin.json`, `policies/`, `package.json`, `README.md`, `CHANGELOG.md`, `LICENSE`.

Verification (local, against OpenClaw 2026.4.14)

  • `npm test` — 107/107 passing
  • `npm pack` tgz contains no test files
  • `openclaw plugins install ./axonflow-openclaw-1.2.3.tgz` installs cleanly with no warnings or blocks
  • Plugin loads and connects to AxonFlow correctly

Test plan

  • CI green
  • After merge + tag v1.2.3, verify `openclaw plugins install @axonflow/openclaw` works end-to-end via ClawHub on 2026.4.14

Changed files

  • .clawhubignore (added, +77/-0)
  • CHANGELOG.md (modified, +9/-0)
  • clawhub/1.4.0/SKILL.md (modified, +1/-14)
  • package.json (modified, +1/-1)
  • src/index.ts (modified, +1/-1)

Code Example

openclaw plugins install @axonflow/openclaw@1.2.1

---

Resolving clawhub:@axonflow/openclaw@1.2.1ClawHub code-plugin @axonflow/openclaw@1.2.1 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'

---

openclaw plugins install mywallet  # works

---

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);

---

// Option A: sanitize the filename
const archivePath = path.join(tmpDir, `${params.name.replace(/\//g, '_')}.zip`);

---

// 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);

---

npm pack @scope/name
openclaw plugins install ./scope-name-VERSION.tgz
RAW_BUFFERClick to expand / collapse

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

openclaw plugins install @axonflow/[email protected]

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

extent analysis

TL;DR

The issue can be fixed by modifying the archivePath creation to either sanitize the filename or ensure the parent directory exists before writing.

Guidance

  • The root cause of the issue is the path.join function resolving archivePath to a path containing a subdirectory that is not created by fs.mkdtemp.
  • To verify the fix, attempt to install a scoped package using openclaw plugins install <scoped-package>.
  • Two possible solutions are provided: sanitizing the filename by replacing / with _ or ensuring the parent directory exists before writing using fs.mkdir with the recursive option.
  • Users can workaround the issue by packing the scoped package using npm pack and then installing the resulting tarball using openclaw plugins install.

Example

// 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);

Notes

The provided fix options assume that the issue is solely due to the path.join resolution and fs.mkdtemp not creating the subdirectory. If other parts of the code rely on the existing path layout, Option B may be a safer choice.

Recommendation

Apply workaround: until the fix is implemented, users can use the provided workaround of packing the scoped package and installing the resulting tarball. This allows for continued use of scoped packages on ClawHub.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Scoped npm packages from ClawHub fail to install with ENOENT [1 pull requests, 1 comments, 1 participants]