From b479542269fcfddf4ac1e95ef2f19c1fae4d71bd Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 3 Nov 2026 20:03:10 -0800 Subject: [PATCH] fix: accept null error and incomplete_details in json responses --- src/protocol/iterator.ts | 4 ++-- src/responses.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/protocol/iterator.ts b/src/protocol/iterator.ts index ec157ad..1911e7c 100644 --- a/src/protocol/iterator.ts +++ b/src/protocol/iterator.ts @@ -585,8 +585,8 @@ const CompleteResponse = type({ "usage?": ResponsesUsage, "status?": "'completed' | 'failed' | 'incomplete' | 'in_progress' | 'cancelled' | 'queued'", - "error?": { "message?": "string" }, - "incomplete_details?": { "reason?": "string" }, + "error?": type({ "message?": "string" }).or("null"), + "incomplete_details?": type({ "reason?": "string" }).or("null"), }); export function parseJSONResponse( diff --git a/src/responses.test.ts b/src/responses.test.ts index 52f90ca..78e448f 100644 --- a/src/responses.test.ts +++ b/src/responses.test.ts @@ -441,6 +441,28 @@ describe("Responses parser — non-streaming failure states", () => { expect(events.some((e) => e.type === "inference.usage")).toBe(true); }); + // OpenAI sends both fields as null on every completed body. + test("decodes a completed body with null error and incomplete_details", () => { + const adapter = createOpenAIResponsesAdapter(source, {}); + const completed = JSON.stringify({ + output: [ + { + type: "message", + content: [{ type: "output_text", text: "hello" }], + }, + ], + status: "completed", + error: null, + incomplete_details: null, + usage: { input_tokens: 10, output_tokens: 5 }, + }); + const events = adapter.parseJSONResponse(completed); + expect(events.map((e) => e.type)).toEqual([ + "inference.text.delta", + "inference.usage", + ]); + }); + test("throws on a response missing the required output field", () => { const adapter = createOpenAIResponsesAdapter(source, {}); expect(() =>