mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-29 22:44:45 -07:00
Improve error messages on failed onboarding (#17357)
This commit is contained in:
@@ -10,9 +10,12 @@ import type {
|
||||
LoadCodeAssistResponse,
|
||||
OnboardUserRequest,
|
||||
} from './types.js';
|
||||
import { UserTierId } from './types.js';
|
||||
import { UserTierId, IneligibleTierReasonCode } from './types.js';
|
||||
import { CodeAssistServer } from './server.js';
|
||||
import type { AuthClient } from 'google-auth-library';
|
||||
import type { ValidationHandler } from '../fallback/types.js';
|
||||
import { ChangeAuthRequestedError } from '../utils/errors.js';
|
||||
import { ValidationRequiredError } from '../utils/googleQuotaErrors.js';
|
||||
|
||||
export class ProjectIdRequiredError extends Error {
|
||||
constructor() {
|
||||
@@ -22,6 +25,16 @@ export class ProjectIdRequiredError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when user cancels the validation process.
|
||||
* This is a non-recoverable error that should result in auth failure.
|
||||
*/
|
||||
export class ValidationCancelledError extends Error {
|
||||
constructor() {
|
||||
super('User cancelled account validation');
|
||||
}
|
||||
}
|
||||
|
||||
export interface UserData {
|
||||
projectId: string;
|
||||
userTier: UserTierId;
|
||||
@@ -33,7 +46,10 @@ export interface UserData {
|
||||
* @param projectId the user's project id, if any
|
||||
* @returns the user's actual project id
|
||||
*/
|
||||
export async function setupUser(client: AuthClient): Promise<UserData> {
|
||||
export async function setupUser(
|
||||
client: AuthClient,
|
||||
validationHandler?: ValidationHandler,
|
||||
): Promise<UserData> {
|
||||
const projectId =
|
||||
process.env['GOOGLE_CLOUD_PROJECT'] ||
|
||||
process.env['GOOGLE_CLOUD_PROJECT_ID'] ||
|
||||
@@ -52,13 +68,36 @@ export async function setupUser(client: AuthClient): Promise<UserData> {
|
||||
pluginType: 'GEMINI',
|
||||
};
|
||||
|
||||
const loadRes = await caServer.loadCodeAssist({
|
||||
cloudaicompanionProject: projectId,
|
||||
metadata: {
|
||||
...coreClientMetadata,
|
||||
duetProject: projectId,
|
||||
},
|
||||
});
|
||||
let loadRes: LoadCodeAssistResponse;
|
||||
while (true) {
|
||||
loadRes = await caServer.loadCodeAssist({
|
||||
cloudaicompanionProject: projectId,
|
||||
metadata: {
|
||||
...coreClientMetadata,
|
||||
duetProject: projectId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
validateLoadCodeAssistResponse(loadRes);
|
||||
break;
|
||||
} catch (e) {
|
||||
if (e instanceof ValidationRequiredError && validationHandler) {
|
||||
const intent = await validationHandler(
|
||||
e.validationLink,
|
||||
e.validationDescription,
|
||||
);
|
||||
if (intent === 'verify') {
|
||||
continue;
|
||||
}
|
||||
if (intent === 'change_auth') {
|
||||
throw new ChangeAuthRequestedError();
|
||||
}
|
||||
throw new ValidationCancelledError();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadRes.currentTier) {
|
||||
if (!loadRes.cloudaicompanionProject) {
|
||||
@@ -139,3 +178,34 @@ function getOnboardTier(res: LoadCodeAssistResponse): GeminiUserTier {
|
||||
userDefinedCloudaicompanionProject: true,
|
||||
};
|
||||
}
|
||||
|
||||
function validateLoadCodeAssistResponse(res: LoadCodeAssistResponse): void {
|
||||
if (!res) {
|
||||
throw new Error('LoadCodeAssist returned empty response');
|
||||
}
|
||||
if (
|
||||
!res.currentTier &&
|
||||
res.ineligibleTiers &&
|
||||
res.ineligibleTiers.length > 0
|
||||
) {
|
||||
// Check for VALIDATION_REQUIRED first - this is a recoverable state
|
||||
const validationTier = res.ineligibleTiers.find(
|
||||
(t) =>
|
||||
t.validationUrl &&
|
||||
t.reasonCode === IneligibleTierReasonCode.VALIDATION_REQUIRED,
|
||||
);
|
||||
const validationUrl = validationTier?.validationUrl;
|
||||
if (validationTier && validationUrl) {
|
||||
throw new ValidationRequiredError(
|
||||
`Account validation required: ${validationTier.reasonMessage}`,
|
||||
undefined,
|
||||
validationUrl,
|
||||
validationTier.reasonMessage,
|
||||
);
|
||||
}
|
||||
|
||||
// For other ineligibility reasons, throw a generic error
|
||||
const reasons = res.ineligibleTiers.map((t) => t.reasonMessage).join(', ');
|
||||
throw new Error(reasons);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user