mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-14 03:50:49 -07:00
feat: better error messages (#20577)
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { parseGoogleApiError, type ErrorInfo } from './googleErrors.js';
|
||||
|
||||
interface GaxiosError {
|
||||
response?: {
|
||||
data?: unknown;
|
||||
@@ -107,6 +109,17 @@ export class CanceledError extends Error {
|
||||
}
|
||||
|
||||
export class ForbiddenError extends Error {}
|
||||
export class AccountSuspendedError extends ForbiddenError {
|
||||
readonly appealUrl?: string;
|
||||
readonly appealLinkText?: string;
|
||||
|
||||
constructor(message: string, metadata?: Record<string, string>) {
|
||||
super(message);
|
||||
this.name = 'AccountSuspendedError';
|
||||
this.appealUrl = metadata?.['appeal_url'];
|
||||
this.appealLinkText = metadata?.['appeal_url_link_text'];
|
||||
}
|
||||
}
|
||||
export class UnauthorizedError extends Error {}
|
||||
export class BadRequestError extends Error {}
|
||||
|
||||
@@ -157,6 +170,24 @@ function isResponseData(data: unknown): data is ResponseData {
|
||||
}
|
||||
|
||||
export function toFriendlyError(error: unknown): unknown {
|
||||
// First, try structured parsing for TOS_VIOLATION detection.
|
||||
const googleApiError = parseGoogleApiError(error);
|
||||
if (googleApiError && googleApiError.code === 403) {
|
||||
const tosDetail = googleApiError.details.find(
|
||||
(d): d is ErrorInfo =>
|
||||
d['@type'] === 'type.googleapis.com/google.rpc.ErrorInfo' &&
|
||||
'reason' in d &&
|
||||
d.reason === 'TOS_VIOLATION',
|
||||
);
|
||||
if (tosDetail) {
|
||||
return new AccountSuspendedError(
|
||||
googleApiError.message,
|
||||
tosDetail.metadata,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to basic Gaxios error parsing for other HTTP errors.
|
||||
if (isGaxiosError(error)) {
|
||||
const data = parseResponseData(error);
|
||||
if (data && data.error && data.error.message && data.error.code) {
|
||||
@@ -166,9 +197,6 @@ export function toFriendlyError(error: unknown): unknown {
|
||||
case 401:
|
||||
return new UnauthorizedError(data.error.message);
|
||||
case 403:
|
||||
// It's import to pass the message here since it might
|
||||
// explain the cause like "the cloud project you're
|
||||
// using doesn't have code assist enabled".
|
||||
return new ForbiddenError(data.error.message);
|
||||
default:
|
||||
}
|
||||
@@ -177,6 +205,13 @@ export function toFriendlyError(error: unknown): unknown {
|
||||
return error;
|
||||
}
|
||||
|
||||
export function isAccountSuspendedError(
|
||||
error: unknown,
|
||||
): AccountSuspendedError | null {
|
||||
const friendly = toFriendlyError(error);
|
||||
return friendly instanceof AccountSuspendedError ? friendly : null;
|
||||
}
|
||||
|
||||
function parseResponseData(error: GaxiosError): ResponseData | undefined {
|
||||
let data = error.response?.data;
|
||||
// Inexplicably, Gaxios sometimes doesn't JSONify the response data.
|
||||
|
||||
Reference in New Issue
Block a user