mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 21:03:05 -07:00
refactor(core): remove unsafe type assertions in error utils (Phase 1.1) (#19750)
This commit is contained in:
@@ -10,6 +10,16 @@ interface GaxiosError {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isGaxiosError(error: unknown): error is GaxiosError {
|
||||||
|
return (
|
||||||
|
typeof error === 'object' &&
|
||||||
|
error !== null &&
|
||||||
|
'response' in error &&
|
||||||
|
typeof (error as { response: unknown }).response === 'object' &&
|
||||||
|
(error as { response: unknown }).response !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
export function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
||||||
return error instanceof Error && 'code' in error;
|
return error instanceof Error && 'code' in error;
|
||||||
}
|
}
|
||||||
@@ -105,11 +115,41 @@ interface ResponseData {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isResponseData(data: unknown): data is ResponseData {
|
||||||
|
if (typeof data !== 'object' || data === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const candidate = data as ResponseData;
|
||||||
|
if (!('error' in candidate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const error = candidate.error;
|
||||||
|
if (typeof error !== 'object' || error === null) {
|
||||||
|
return false; // error property exists but is not an object (could be undefined, but we checked 'in')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional properties check
|
||||||
|
if (
|
||||||
|
'code' in error &&
|
||||||
|
typeof error.code !== 'number' &&
|
||||||
|
error.code !== undefined
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
'message' in error &&
|
||||||
|
typeof error.message !== 'string' &&
|
||||||
|
error.message !== undefined
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export function toFriendlyError(error: unknown): unknown {
|
export function toFriendlyError(error: unknown): unknown {
|
||||||
if (error && typeof error === 'object' && 'response' in error) {
|
if (isGaxiosError(error)) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
const data = parseResponseData(error);
|
||||||
const gaxiosError = error as GaxiosError;
|
|
||||||
const data = parseResponseData(gaxiosError);
|
|
||||||
if (data && data.error && data.error.message && data.error.code) {
|
if (data && data.error && data.error.message && data.error.code) {
|
||||||
switch (data.error.code) {
|
switch (data.error.code) {
|
||||||
case 400:
|
case 400:
|
||||||
@@ -129,17 +169,20 @@ export function toFriendlyError(error: unknown): unknown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseResponseData(error: GaxiosError): ResponseData | undefined {
|
function parseResponseData(error: GaxiosError): ResponseData | undefined {
|
||||||
|
let data = error.response?.data;
|
||||||
// Inexplicably, Gaxios sometimes doesn't JSONify the response data.
|
// Inexplicably, Gaxios sometimes doesn't JSONify the response data.
|
||||||
if (typeof error.response?.data === 'string') {
|
if (typeof data === 'string') {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
data = JSON.parse(data);
|
||||||
return JSON.parse(error.response?.data) as ResponseData;
|
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
||||||
return error.response?.data as ResponseData | undefined;
|
if (isResponseData(data)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,8 +195,15 @@ function parseResponseData(error: GaxiosError): ResponseData | undefined {
|
|||||||
export function isAuthenticationError(error: unknown): boolean {
|
export function isAuthenticationError(error: unknown): boolean {
|
||||||
// Check for MCP SDK errors with code property
|
// Check for MCP SDK errors with code property
|
||||||
// (SseError and StreamableHTTPError both have numeric 'code' property)
|
// (SseError and StreamableHTTPError both have numeric 'code' property)
|
||||||
if (error && typeof error === 'object' && 'code' in error) {
|
if (
|
||||||
const errorCode = (error as { code: unknown }).code;
|
error &&
|
||||||
|
typeof error === 'object' &&
|
||||||
|
'code' in error &&
|
||||||
|
typeof (error as { code: unknown }).code === 'number'
|
||||||
|
) {
|
||||||
|
// Safe access after check
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
|
const errorCode = (error as { code: number }).code;
|
||||||
if (errorCode === 401) {
|
if (errorCode === 401) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,9 +207,13 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
|
|||||||
detailObj['@type'] = detailObj[typeKey];
|
detailObj['@type'] = detailObj[typeKey];
|
||||||
delete detailObj[typeKey];
|
delete detailObj[typeKey];
|
||||||
}
|
}
|
||||||
// We can just cast it; the consumer will have to switch on @type
|
// Basic structural check before casting.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
// Since the proto definitions are loose, we primarily rely on @type presence.
|
||||||
details.push(detailObj as unknown as GoogleApiErrorDetail);
|
if (typeof detailObj['@type'] === 'string') {
|
||||||
|
// We can just cast it; the consumer will have to switch on @type
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
|
details.push(detailObj as unknown as GoogleApiErrorDetail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,6 +229,16 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isErrorShape(obj: unknown): obj is ErrorShape {
|
||||||
|
return (
|
||||||
|
typeof obj === 'object' &&
|
||||||
|
obj !== null &&
|
||||||
|
(('message' in obj &&
|
||||||
|
typeof (obj as { message: unknown }).message === 'string') ||
|
||||||
|
('code' in obj && typeof (obj as { code: unknown }).code === 'number'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function fromGaxiosError(errorObj: object): ErrorShape | undefined {
|
function fromGaxiosError(errorObj: object): ErrorShape | undefined {
|
||||||
const gaxiosError = errorObj as {
|
const gaxiosError = errorObj as {
|
||||||
response?: {
|
response?: {
|
||||||
@@ -260,7 +274,10 @@ function fromGaxiosError(errorObj: object): ErrorShape | undefined {
|
|||||||
if (typeof data === 'object' && data !== null) {
|
if (typeof data === 'object' && data !== null) {
|
||||||
if ('error' in data) {
|
if ('error' in data) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
outerError = (data as { error: ErrorShape }).error;
|
const potentialError = (data as { error: unknown }).error;
|
||||||
|
if (isErrorShape(potentialError)) {
|
||||||
|
outerError = potentialError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,7 +337,10 @@ function fromApiError(errorObj: object): ErrorShape | undefined {
|
|||||||
if (typeof data === 'object' && data !== null) {
|
if (typeof data === 'object' && data !== null) {
|
||||||
if ('error' in data) {
|
if ('error' in data) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
outerError = (data as { error: ErrorShape }).error;
|
const potentialError = (data as { error: unknown }).error;
|
||||||
|
if (isErrorShape(potentialError)) {
|
||||||
|
outerError = potentialError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user