From a1367e9cdd2773ef27da8c307342f904875e8aa8 Mon Sep 17 00:00:00 2001 From: Sehoon Shon Date: Fri, 27 Feb 2026 18:57:32 -0500 Subject: [PATCH] fix(core): parse raw ASCII buffer strings in Gaxios errors (#20626) --- .../src/core/loggingContentGenerator.test.ts | 72 +++++++++++++++++++ .../core/src/core/loggingContentGenerator.ts | 32 +++++++++ 2 files changed, 104 insertions(+) diff --git a/packages/core/src/core/loggingContentGenerator.test.ts b/packages/core/src/core/loggingContentGenerator.test.ts index fc9103491c..5c0db58353 100644 --- a/packages/core/src/core/loggingContentGenerator.test.ts +++ b/packages/core/src/core/loggingContentGenerator.test.ts @@ -243,6 +243,78 @@ describe('LoggingContentGenerator', () => { expect(errorEvent.error_type).toBe('FatalAuthenticationError'); }); }); + + describe('Gaxios error parsing', () => { + it('should parse raw ASCII buffer strings in Gaxios errors', async () => { + const req = { contents: [], model: 'gemini-pro' }; + + // Simulate a Gaxios error with comma-separated ASCII codes + const asciiData = '72,101,108,108,111'; // "Hello" + const gaxiosError = Object.assign(new Error('Gaxios Error'), { + response: { data: asciiData }, + }); + + vi.mocked(wrapped.generateContent).mockRejectedValue(gaxiosError); + + await expect( + loggingContentGenerator.generateContent( + req, + 'prompt-123', + LlmRole.MAIN, + ), + ).rejects.toSatisfy((error: unknown) => { + const gError = error as { response: { data: unknown } }; + expect(gError.response.data).toBe('Hello'); + return true; + }); + }); + + it('should leave data alone if it is not a comma-separated string', async () => { + const req = { contents: [], model: 'gemini-pro' }; + + const normalData = 'Normal error message'; + const gaxiosError = Object.assign(new Error('Gaxios Error'), { + response: { data: normalData }, + }); + + vi.mocked(wrapped.generateContent).mockRejectedValue(gaxiosError); + + await expect( + loggingContentGenerator.generateContent( + req, + 'prompt-123', + LlmRole.MAIN, + ), + ).rejects.toSatisfy((error: unknown) => { + const gError = error as { response: { data: unknown } }; + expect(gError.response.data).toBe(normalData); + return true; + }); + }); + + it('should leave data alone if parsing fails', async () => { + const req = { contents: [], model: 'gemini-pro' }; + + const invalidAscii = '72,invalid,101'; + const gaxiosError = Object.assign(new Error('Gaxios Error'), { + response: { data: invalidAscii }, + }); + + vi.mocked(wrapped.generateContent).mockRejectedValue(gaxiosError); + + await expect( + loggingContentGenerator.generateContent( + req, + 'prompt-123', + LlmRole.MAIN, + ), + ).rejects.toSatisfy((error: unknown) => { + const gError = error as { response: { data: unknown } }; + expect(gError.response.data).toBe(invalidAscii); + return true; + }); + }); + }); }); describe('generateContentStream', () => { diff --git a/packages/core/src/core/loggingContentGenerator.ts b/packages/core/src/core/loggingContentGenerator.ts index 5679c03a52..027a3a24ad 100644 --- a/packages/core/src/core/loggingContentGenerator.ts +++ b/packages/core/src/core/loggingContentGenerator.ts @@ -274,6 +274,32 @@ export class LoggingContentGenerator implements ContentGenerator { logApiResponse(this.config, event); } + private _fixGaxiosErrorData(error: unknown): void { + // Fix for raw ASCII buffer strings appearing in dev with the latest + // Gaxios updates. + if ( + typeof error === 'object' && + error !== null && + 'response' in error && + typeof error.response === 'object' && + error.response !== null && + 'data' in error.response + ) { + const response = error.response as { data: unknown }; + const data = response.data; + if (typeof data === 'string' && data.includes(',')) { + try { + const charCodes = data.split(',').map(Number); + if (charCodes.every((code) => !isNaN(code))) { + response.data = String.fromCharCode(...charCodes); + } + } catch (_e) { + // If parsing fails, just leave it alone + } + } + } + } + private _logApiError( durationMs: number, error: unknown, @@ -380,6 +406,9 @@ export class LoggingContentGenerator implements ContentGenerator { } catch (error) { spanMetadata.error = error; const durationMs = Date.now() - startTime; + + this._fixGaxiosErrorData(error); + this._logApiError( durationMs, error, @@ -447,6 +476,9 @@ export class LoggingContentGenerator implements ContentGenerator { ); } catch (error) { const durationMs = Date.now() - startTime; + + this._fixGaxiosErrorData(error); + this._logApiError( durationMs, error,