fix(core): parse raw ASCII buffer strings in Gaxios errors (#20626)

This commit is contained in:
Sehoon Shon
2026-02-27 18:57:32 -05:00
committed by GitHub
parent c89d4f9c6c
commit a1367e9cdd
2 changed files with 104 additions and 0 deletions

View File

@@ -243,6 +243,78 @@ describe('LoggingContentGenerator', () => {
expect(errorEvent.error_type).toBe('FatalAuthenticationError'); 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', () => { describe('generateContentStream', () => {

View File

@@ -274,6 +274,32 @@ export class LoggingContentGenerator implements ContentGenerator {
logApiResponse(this.config, event); 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( private _logApiError(
durationMs: number, durationMs: number,
error: unknown, error: unknown,
@@ -380,6 +406,9 @@ export class LoggingContentGenerator implements ContentGenerator {
} catch (error) { } catch (error) {
spanMetadata.error = error; spanMetadata.error = error;
const durationMs = Date.now() - startTime; const durationMs = Date.now() - startTime;
this._fixGaxiosErrorData(error);
this._logApiError( this._logApiError(
durationMs, durationMs,
error, error,
@@ -447,6 +476,9 @@ export class LoggingContentGenerator implements ContentGenerator {
); );
} catch (error) { } catch (error) {
const durationMs = Date.now() - startTime; const durationMs = Date.now() - startTime;
this._fixGaxiosErrorData(error);
this._logApiError( this._logApiError(
durationMs, durationMs,
error, error,