From eb07aba9731b2f365a29d2e4f01df8f4a65d44e8 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 15 May 2026 08:02:13 +0800 Subject: [PATCH] fix(clawhub): wrap malformed marketplace json --- CHANGELOG.md | 1 + src/infra/clawhub.test.ts | 13 +++++++++++++ src/infra/clawhub.ts | 6 +++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78370b37fa8..418ebe93b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Docs: https://docs.openclaw.ai - Twilio voice-call: report malformed media stream WebSocket JSON with an owned parser error instead of logging raw parser failures. - Tlon/Urbit: report malformed SSE event JSON with an owned parser error instead of logging raw parser failures. - Signal: return a stable installer error when GitHub release metadata is malformed JSON. +- ClawHub: report malformed successful marketplace JSON responses with owned errors instead of leaking raw parser failures. - Matrix: ignore malformed percent-encoding in optional location URI parameters instead of letting a bad `geo:` event abort inbound message handling. - Web search: auto-detect Brave through its legacy `tools.web.search.apiKey` compatibility fallback while keeping doctor migration to `plugins.entries.brave.config.webSearch.apiKey` as the canonical repair, so allowlisted isolated cron runs do not report `web_search` unavailable before migration. Fixes #81538. Thanks @atomicmonk. - Plugins: memoize repeated in-process plugin metadata snapshots and keep vanished managed-install residue from forcing full derived discovery, reducing gateway/status startup scans under large plugin sets. Fixes #81143 and #79806. (#81570) Thanks @Kaspre, @holgergruenhagen, @JanPlessow, and @mjamiv. diff --git a/src/infra/clawhub.test.ts b/src/infra/clawhub.test.ts index 0e6f4144fed..d1bcaf2f831 100644 --- a/src/infra/clawhub.test.ts +++ b/src/infra/clawhub.test.ts @@ -483,6 +483,19 @@ describe("clawhub helpers", () => { ).rejects.toThrow(/Rate limit exceeded Sign in for higher rate limits\.$/); }); + it("wraps malformed successful ClawHub JSON responses", async () => { + await expect( + searchClawHubSkills({ + query: "calendar", + fetchImpl: async () => + new Response("{not json", { + status: 200, + headers: { "content-type": "application/json" }, + }), + }), + ).rejects.toThrow("ClawHub /api/v1/search returned malformed JSON"); + }); + it("annotates 429 errors with the reset hint but no sign-in hint when authenticated", async () => { process.env.OPENCLAW_CLAWHUB_TOKEN = "env-token-123"; await expect( diff --git a/src/infra/clawhub.ts b/src/infra/clawhub.ts index 89de7fd6dec..8e79cf118fd 100644 --- a/src/infra/clawhub.ts +++ b/src/infra/clawhub.ts @@ -634,7 +634,11 @@ async function fetchJson(params: ClawHubRequestParams): Promise { if (!response.ok) { throw await buildClawHubError(response, url, hasToken); } - return (await response.json()) as T; + try { + return (await response.json()) as T; + } catch (cause) { + throw new Error(`ClawHub ${url.pathname} returned malformed JSON`, { cause }); + } } async function readClawHubResponseBytes(params: {