From 36689751c3c8a9872f84cc916a6322b24942319e Mon Sep 17 00:00:00 2001 From: Mason Huang Date: Wed, 29 Apr 2026 17:15:43 +0800 Subject: [PATCH] fix: cover plain streaming json parse failures --- ...i-embedded-helpers.formatassistanterrortext.test.ts | 8 ++++++++ src/agents/pi-embedded-helpers/errors.test.ts | 10 ++++++++++ .../pi-embedded-helpers/sanitize-user-facing-text.ts | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts b/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts index 34a520f5b6e..78b6091c96a 100644 --- a/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts +++ b/src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts @@ -483,6 +483,14 @@ describe("sanitizeUserFacingText — streaming JSON parse error (#59076)", () => expect(result).toBe("LLM streaming response contained a malformed fragment. Please try again."); }); + it.each([ + "Unexpected end of JSON input", + "Unexpected non-whitespace character after JSON at position 4", + ])("rewrites plain JSON.parse error variants in error context: %s", (text) => { + const result = sanitizeUserFacingText(text, { errorContext: true }); + expect(result).toBe("LLM streaming response contained a malformed fragment. Please try again."); + }); + it("does not rewrite JSON parse error when not in error context", () => { // When not in error context, the text could be legitimate assistant content // mentioning JSON errors. Don't rewrite. diff --git a/src/agents/pi-embedded-helpers/errors.test.ts b/src/agents/pi-embedded-helpers/errors.test.ts index 53cee0368c2..e8cd0d9ffd8 100644 --- a/src/agents/pi-embedded-helpers/errors.test.ts +++ b/src/agents/pi-embedded-helpers/errors.test.ts @@ -19,6 +19,16 @@ describe("formatAssistantErrorText streaming JSON parse classification", () => { ); }); + it.each([ + "Unexpected end of JSON input", + "Unexpected non-whitespace character after JSON at position 4", + ])("suppresses plain JSON.parse streaming fragment failures: %s", (errorMessage) => { + const msg = makeAssistantError(errorMessage); + expect(formatAssistantErrorText(msg)).toBe( + "LLM streaming response contained a malformed fragment. Please try again.", + ); + }); + it("suppresses structured Anthropic tool-call delta parse failures", () => { const msg = makeAssistantError( 'Could not parse Anthropic SSE event content_block_delta: Unexpected end of JSON input; data={"type":"content_block_delta","delta":{"type":"input_json_delta","partial_json":"{\\"path\\":"},"index":0}', diff --git a/src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts b/src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts index 378e0e6639c..1091b5c2984 100644 --- a/src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts +++ b/src/agents/pi-embedded-helpers/sanitize-user-facing-text.ts @@ -223,7 +223,7 @@ export function isStreamingJsonParseError(raw: string): boolean { return true; } - return /^(?:Expected (?:',' or '\}' after property value|double-quoted property name|':' after property name|',' or '\]' after array element)|Unterminated string) in JSON at position \d+(?: \(line \d+ column \d+\))?$/i.test( + return /^(?:Unexpected end of JSON input|Unexpected non-whitespace character after JSON at position \d+(?: \(line \d+ column \d+\))?|(?:Expected (?:',' or '\}' after property value|double-quoted property name|':' after property name|',' or '\]' after array element)|Unterminated string) in JSON at position \d+(?: \(line \d+ column \d+\))?)$/i.test( trimmed, ); }