mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-24 03:54:43 -07:00
feat(core): increase fetch timeout and fix [object Object] error stringification (#20441)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
This commit is contained in:
@@ -73,17 +73,19 @@ describe('CodeAssistServer', () => {
|
|||||||
LlmRole.MAIN,
|
LlmRole.MAIN,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(mockRequest).toHaveBeenCalledWith({
|
expect(mockRequest).toHaveBeenCalledWith(
|
||||||
url: expect.stringContaining(':generateContent'),
|
expect.objectContaining({
|
||||||
method: 'POST',
|
url: expect.stringContaining(':generateContent'),
|
||||||
headers: {
|
method: 'POST',
|
||||||
'Content-Type': 'application/json',
|
headers: {
|
||||||
'x-custom-header': 'test-value',
|
'Content-Type': 'application/json',
|
||||||
},
|
'x-custom-header': 'test-value',
|
||||||
responseType: 'json',
|
},
|
||||||
body: expect.any(String),
|
responseType: 'json',
|
||||||
signal: undefined,
|
body: expect.any(String),
|
||||||
});
|
signal: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const requestBody = JSON.parse(mockRequest.mock.calls[0][0].body);
|
const requestBody = JSON.parse(mockRequest.mock.calls[0][0].body);
|
||||||
expect(requestBody.user_prompt_id).toBe('user-prompt-id');
|
expect(requestBody.user_prompt_id).toBe('user-prompt-id');
|
||||||
@@ -391,17 +393,19 @@ describe('CodeAssistServer', () => {
|
|||||||
results.push(res);
|
results.push(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(mockRequest).toHaveBeenCalledWith({
|
expect(mockRequest).toHaveBeenCalledWith(
|
||||||
url: expect.stringContaining(':streamGenerateContent'),
|
expect.objectContaining({
|
||||||
method: 'POST',
|
url: expect.stringContaining(':streamGenerateContent'),
|
||||||
params: { alt: 'sse' },
|
method: 'POST',
|
||||||
responseType: 'stream',
|
params: { alt: 'sse' },
|
||||||
body: expect.any(String),
|
responseType: 'stream',
|
||||||
headers: {
|
body: expect.any(String),
|
||||||
'Content-Type': 'application/json',
|
headers: {
|
||||||
},
|
'Content-Type': 'application/json',
|
||||||
signal: undefined,
|
},
|
||||||
});
|
signal: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
expect(results).toHaveLength(2);
|
expect(results).toHaveLength(2);
|
||||||
expect(results[0].candidates?.[0].content?.parts?.[0].text).toBe('Hello');
|
expect(results[0].candidates?.[0].content?.parts?.[0].text).toBe('Hello');
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ export function getErrorMessage(error: unknown): string {
|
|||||||
if (friendlyError instanceof Error) {
|
if (friendlyError instanceof Error) {
|
||||||
return friendlyError.message;
|
return friendlyError.message;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
typeof friendlyError === 'object' &&
|
||||||
|
friendlyError !== null &&
|
||||||
|
'message' in friendlyError &&
|
||||||
|
typeof (friendlyError as { message: unknown }).message === 'string'
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
|
return (friendlyError as { message: string }).message;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return String(friendlyError);
|
return String(friendlyError);
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2026 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { getErrorMessage } from './errors.js';
|
||||||
|
import { type HttpError } from './httpErrors.js';
|
||||||
|
|
||||||
|
describe('getErrorMessage with timeout errors', () => {
|
||||||
|
it('should handle undici HeadersTimeoutError correctly', () => {
|
||||||
|
// Simulate what undici might throw if it's not a proper Error instance
|
||||||
|
// or has a specific code.
|
||||||
|
const timeoutError = {
|
||||||
|
name: 'HeadersTimeoutError',
|
||||||
|
code: 'UND_ERR_HEADERS_TIMEOUT',
|
||||||
|
message: 'Headers timeout error',
|
||||||
|
};
|
||||||
|
|
||||||
|
// If it's a plain object, getErrorMessage might struggle if it expects an Error
|
||||||
|
const message = getErrorMessage(timeoutError);
|
||||||
|
// Based on existing implementation:
|
||||||
|
// friendlyError = toFriendlyError(timeoutError) -> returns timeoutError
|
||||||
|
// if (friendlyError instanceof Error) -> false
|
||||||
|
// return String(friendlyError) -> "[object Object]"
|
||||||
|
|
||||||
|
expect(message).toBe('Headers timeout error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle undici HeadersTimeoutError as an Error instance', () => {
|
||||||
|
const error = new Error('Headers timeout error');
|
||||||
|
(error as HttpError).name = 'HeadersTimeoutError';
|
||||||
|
(error as HttpError).status = 504; // simulate status for test
|
||||||
|
(error as HttpError & { code?: string }).code = 'UND_ERR_HEADERS_TIMEOUT';
|
||||||
|
|
||||||
|
const message = getErrorMessage(error);
|
||||||
|
expect(message).toBe('Headers timeout error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return String representation for objects without a message property', () => {
|
||||||
|
const error = { some: 'other', object: 123 };
|
||||||
|
const message = getErrorMessage(error);
|
||||||
|
expect(message).toBe('[object Object]');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,7 +6,18 @@
|
|||||||
|
|
||||||
import { getErrorMessage, isNodeError } from './errors.js';
|
import { getErrorMessage, isNodeError } from './errors.js';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { ProxyAgent, setGlobalDispatcher } from 'undici';
|
import { Agent, ProxyAgent, setGlobalDispatcher } from 'undici';
|
||||||
|
|
||||||
|
const DEFAULT_HEADERS_TIMEOUT = 60000; // 60 seconds
|
||||||
|
const DEFAULT_BODY_TIMEOUT = 300000; // 5 minutes
|
||||||
|
|
||||||
|
// Configure default global dispatcher with higher timeouts
|
||||||
|
setGlobalDispatcher(
|
||||||
|
new Agent({
|
||||||
|
headersTimeout: DEFAULT_HEADERS_TIMEOUT,
|
||||||
|
bodyTimeout: DEFAULT_BODY_TIMEOUT,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const PRIVATE_IP_RANGES = [
|
const PRIVATE_IP_RANGES = [
|
||||||
/^10\./,
|
/^10\./,
|
||||||
@@ -73,5 +84,11 @@ export async function fetchWithTimeout(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setGlobalProxy(proxy: string) {
|
export function setGlobalProxy(proxy: string) {
|
||||||
setGlobalDispatcher(new ProxyAgent(proxy));
|
setGlobalDispatcher(
|
||||||
|
new ProxyAgent({
|
||||||
|
uri: proxy,
|
||||||
|
headersTimeout: DEFAULT_HEADERS_TIMEOUT,
|
||||||
|
bodyTimeout: DEFAULT_BODY_TIMEOUT,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user