diff --git a/index.ts b/index.ts index 054d3a4b..054ecd3b 100644 --- a/index.ts +++ b/index.ts @@ -48,6 +48,7 @@ import { resolveOpenClawStateDir } from "./src/utils/openclaw-state-dir.js"; export { GatewayMemoryClient, GatewayMemoryClientError, + GatewayMemoryClientParseError, createGatewayPlatformAdapter, } from "./src/adapters/gateway-client/index.js"; export type { diff --git a/src/adapters/gateway-client/gateway-client.test.ts b/src/adapters/gateway-client/gateway-client.test.ts index f8e5efa8..1820d699 100644 --- a/src/adapters/gateway-client/gateway-client.test.ts +++ b/src/adapters/gateway-client/gateway-client.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { GatewayMemoryClient, GatewayMemoryClientError, + GatewayMemoryClientParseError, createGatewayPlatformAdapter, } from "./index.js"; @@ -58,6 +59,20 @@ describe("GatewayMemoryClient", () => { responseBody: "bad query", } satisfies Partial); }); + + it("surfaces malformed success responses with request context", async () => { + const client = new GatewayMemoryClient({ + baseUrl: "http://127.0.0.1:8420", + fetchImpl: async () => new Response("not-json", { status: 200 }), + }); + + await expect(client.health()).rejects.toMatchObject({ + name: "GatewayMemoryClientParseError", + status: 200, + path: "/health", + responseBody: "not-json", + } satisfies Partial); + }); }); describe("createGatewayPlatformAdapter", () => { diff --git a/src/adapters/gateway-client/index.ts b/src/adapters/gateway-client/index.ts index 1dfbdc83..179a7a09 100644 --- a/src/adapters/gateway-client/index.ts +++ b/src/adapters/gateway-client/index.ts @@ -79,6 +79,20 @@ export class GatewayMemoryClientError extends Error { } } +export class GatewayMemoryClientParseError extends Error { + readonly status: number; + readonly path: string; + readonly responseBody: string; + + constructor(path: string, status: number, responseBody: string) { + super(`Gateway response was not valid JSON: ${path} returned ${status}`); + this.name = "GatewayMemoryClientParseError"; + this.path = path; + this.status = status; + this.responseBody = responseBody; + } +} + export class GatewayMemoryClient { private readonly baseUrl: string; private readonly apiKey: string | undefined; @@ -136,7 +150,11 @@ export class GatewayMemoryClient { if (!response.ok) { throw new GatewayMemoryClientError(path, response.status, text); } - return (text ? JSON.parse(text) : {}) as T; + try { + return (text ? JSON.parse(text) : {}) as T; + } catch { + throw new GatewayMemoryClientParseError(path, response.status, text); + } } finally { clearTimeout(timer); } diff --git a/src/adapters/index.ts b/src/adapters/index.ts index a0f3961c..f22a6bad 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -23,6 +23,7 @@ export type { StandaloneHostAdapterOptions, StandaloneLLMConfig, StandaloneLLMRu export { GatewayMemoryClient, GatewayMemoryClientError, + GatewayMemoryClientParseError, createGatewayPlatformAdapter, } from "./gateway-client/index.js"; export type {