fix(memory): wrap malformed remote json · openclaw/openclaw@f0803c5
vincentkoc
·
2026-05-15
·
via Recent Commits to openclaw:main
File tree
packages/memory-host-sdk/src/host
| Original file line number | Diff line number | Diff line change |
|---|
@@ -92,6 +92,7 @@ Docs: https://docs.openclaw.ai
|
92 | 92 | - Tavily: report malformed search and extract API JSON with provider-owned errors instead of leaking raw parser failures. |
93 | 93 | - Perplexity: report malformed Search API and chat completion JSON with provider-owned errors instead of leaking raw parser failures. |
94 | 94 | - Exa: report malformed search API JSON with a provider-owned error instead of leaking raw parser failures. |
| 95 | +- Memory host SDK: report malformed remote JSON with caller-scoped errors for POST and batch file upload responses instead of leaking raw parser failures. |
95 | 96 | - Twilio voice-call: report malformed successful API JSON responses with provider-owned errors instead of leaking raw parser failures. |
96 | 97 | - Voice-call provider APIs: report malformed successful guarded JSON responses with provider-prefixed errors instead of leaking raw parser failures. |
97 | 98 | - Realtime transcription: report malformed provider websocket JSON frames with owned parser errors instead of leaking raw `SyntaxError` objects. |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { beforeEach, describe, expect, it, vi } from "vitest"; |
| 2 | +import { uploadBatchJsonlFile } from "./batch-upload.js"; |
| 3 | +import { withRemoteHttpResponse } from "./remote-http.js"; |
| 4 | + |
| 5 | +vi.mock("./remote-http.js", () => ({ |
| 6 | +withRemoteHttpResponse: vi.fn(), |
| 7 | +})); |
| 8 | + |
| 9 | +const remoteHttpMock = vi.mocked(withRemoteHttpResponse); |
| 10 | + |
| 11 | +function textResponse(body: string, status: number): Response { |
| 12 | +return { |
| 13 | +ok: status >= 200 && status < 300, |
| 14 | + status, |
| 15 | +json: async () => JSON.parse(body) as unknown, |
| 16 | +text: async () => body, |
| 17 | +} as Response; |
| 18 | +} |
| 19 | + |
| 20 | +describe("uploadBatchJsonlFile", () => { |
| 21 | +beforeEach(() => { |
| 22 | +vi.clearAllMocks(); |
| 23 | +}); |
| 24 | + |
| 25 | +it("wraps malformed file-upload JSON with the request error prefix", async () => { |
| 26 | +remoteHttpMock.mockImplementationOnce(async (params) => { |
| 27 | +return await params.onResponse(textResponse("{ nope", 200)); |
| 28 | +}); |
| 29 | + |
| 30 | +await expect( |
| 31 | +uploadBatchJsonlFile({ |
| 32 | +client: { |
| 33 | +baseUrl: "https://memory.example/v1", |
| 34 | +headers: { Authorization: "Bearer test" }, |
| 35 | +}, |
| 36 | +requests: [{ input: "one" }], |
| 37 | +errorPrefix: "file upload failed", |
| 38 | +}), |
| 39 | +).rejects.toThrow("file upload failed: malformed JSON response"); |
| 40 | +}); |
| 41 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -34,7 +34,11 @@ export async function uploadBatchJsonlFile(params: {
|
34 | 34 | const text = await fileRes.text(); |
35 | 35 | throw new Error(`${params.errorPrefix}: ${fileRes.status} ${text}`); |
36 | 36 | } |
37 | | -return (await fileRes.json()) as { id?: string }; |
| 37 | +try { |
| 38 | +return (await fileRes.json()) as { id?: string }; |
| 39 | +} catch (cause) { |
| 40 | +throw new Error(`${params.errorPrefix}: malformed JSON response`, { cause }); |
| 41 | +} |
38 | 42 | }, |
39 | 43 | }); |
40 | 44 | if (!filePayload.id) { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -70,4 +70,20 @@ describe("postJson", () => {
|
70 | 70 | expect((error as Error).message).toBe("post failed: 502 bad gateway"); |
71 | 71 | expect((error as { status?: unknown }).status).toBe(502); |
72 | 72 | }); |
| 73 | + |
| 74 | +it("wraps malformed success JSON with the request error prefix", async () => { |
| 75 | +remoteHttpMock.mockImplementationOnce(async (params) => { |
| 76 | +return await params.onResponse(textResponse("{ nope", 200)); |
| 77 | +}); |
| 78 | + |
| 79 | +await expect( |
| 80 | +postJson({ |
| 81 | +url: "https://memory.example/v1/post", |
| 82 | +headers: {}, |
| 83 | +body: {}, |
| 84 | +errorPrefix: "post failed", |
| 85 | +parse: () => ({}), |
| 86 | +}), |
| 87 | +).rejects.toThrow("post failed: malformed JSON response"); |
| 88 | +}); |
73 | 89 | }); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -31,7 +31,15 @@ export async function postJson<T>(params: {
|
31 | 31 | } |
32 | 32 | throw err; |
33 | 33 | } |
34 | | -return await params.parse(await res.json()); |
| 34 | +return await params.parse(await readJsonResponse(res, params.errorPrefix)); |
35 | 35 | }, |
36 | 36 | }); |
37 | 37 | } |
| 38 | + |
| 39 | +async function readJsonResponse(res: Response, errorPrefix: string): Promise<unknown> { |
| 40 | +try { |
| 41 | +return await res.json(); |
| 42 | +} catch (cause) { |
| 43 | +throw new Error(`${errorPrefix}: malformed JSON response`, { cause }); |
| 44 | +} |
| 45 | +} |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。