diff --git a/src/infra/install-source-utils.test.ts b/src/infra/install-source-utils.test.ts index b9f245510c2..64cb804210f 100644 --- a/src/infra/install-source-utils.test.ts +++ b/src/infra/install-source-utils.test.ts @@ -244,6 +244,24 @@ describe("packNpmSpecToArchive", () => { }); }); + it("returns friendly error for 404 (package not on npm)", async () => { + const cwd = await createFixtureDir(); + mockPackCommandResult({ + stdout: "", + stderr: "npm error code E404\nnpm error 404 '@openclaw/whatsapp@*' is not in this registry.", + code: 1, + }); + + const result = await runPack("@openclaw/whatsapp", cwd); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error).toContain("Package not found on npm"); + expect(result.error).toContain("@openclaw/whatsapp"); + expect(result.error).toContain("docs.openclaw.ai/tools/plugin"); + } + }); + it("returns explicit error when npm pack produces no archive name", async () => { const cwd = await createFixtureDir(); mockPackCommandResult({ diff --git a/src/infra/install-source-utils.ts b/src/infra/install-source-utils.ts index 206711db2fc..fce33b61979 100644 --- a/src/infra/install-source-utils.ts +++ b/src/infra/install-source-utils.ts @@ -207,7 +207,14 @@ export async function packNpmSpecToArchive(params: { }, ); if (res.code !== 0) { - return { ok: false, error: `npm pack failed: ${res.stderr.trim() || res.stdout.trim()}` }; + const raw = res.stderr.trim() || res.stdout.trim(); + if (/E404|is not in this registry/i.test(raw)) { + return { + ok: false, + error: `Package not found on npm: ${params.spec}. See https://docs.openclaw.ai/tools/plugin for installable plugins.`, + }; + } + return { ok: false, error: `npm pack failed: ${raw}` }; } const parsedJson = parseNpmPackJsonOutput(res.stdout || "");