can1357/oh-my-pi · error · AIError.ConfigurationError

Google Cloud credentials missing projectId

Error message

Google Cloud credentials missing projectId

What it means

Google Cloud (Gemini CLI) OAuth credentials must include a projectId because token refresh calls refreshGoogleCloudToken(refresh, projectId) — Google's token exchange requires the cloud project context. When stored credentials lack projectId, the registry's refreshToken hook throws this ConfigurationError before making any network call. Login normally populates it, so this indicates incomplete or legacy credentials.

Source

Thrown at packages/ai/src/registry/google-gemini-cli.ts:15

import * as AIError from "../error";
import type { OAuthCredentials, OAuthLoginCallbacks } from "./oauth/types";
import type { ProviderDefinition } from "./types";

export const googleGeminiCliProvider = {
	id: "google-gemini-cli",
	name: "Google Cloud Code Assist (Gemini CLI)",
	login: async (cb: OAuthLoginCallbacks) => {
		// Lazy import: keep heavy OAuth flow modules out of the eager registry graph.
		const { loginGeminiCli } = await import("./oauth/google-gemini-cli");
		return loginGeminiCli(cb);
	},
	refreshToken: async (credentials: OAuthCredentials) => {
		if (!credentials.projectId) {
			throw new AIError.ConfigurationError("Google Cloud credentials missing projectId");
		}
		const { refreshGoogleCloudToken } = await import("./oauth/google-gemini-cli");
		return refreshGoogleCloudToken(credentials.refresh, credentials.projectId);
	},
	callbackPort: 8085,
	pasteCodeFlow: true,
} as const satisfies ProviderDefinition;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-run /login (loginGeminiCli) to regenerate credentials including projectId.
  2. Set the projectId explicitly in the stored credentials if known (the Google Cloud project used at login).
  3. Inspect the stored credential file for a missing/null projectId and restore it.
  4. Ensure the library version that wrote the credentials matches the version reading them (schema drift).

Example fix

// before
await registry.refreshToken({ refresh: "1//abc", projectId: undefined });
// throws ConfigurationError
// after
const creds = await registry.login(callbacks); // captures projectId
await registry.refreshToken(creds);
Defensive patterns

Strategy: try-catch

Validate before calling

// before triggering a refresh
if (!credentials.projectId) {
  await reloginGeminiCli(); // recapture credentials including projectId
}

Type guard

function hasProjectId(c: OAuthCredentials): c is OAuthCredentials & { projectId: string } {
  return typeof c.projectId === "string" && c.projectId.length > 0;
}

Try / catch

try {
  await registry.refreshToken(credentials);
} catch (err) {
  if (err instanceof AIError.ConfigurationError && /missing projectId/.test(err.message)) {
    credentials = await registry.login(callbacks);
    await registry.refreshToken(credentials);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling the registry's refreshToken (or any automatic refresh path) with OAuthCredentials whose projectId is null/undefined — e.g. credentials from an older login flow, hand-edited auth state, or programmatically built credential objects.

Common situations: Stale credentials predating the projectId requirement; copying an auth file between machines with partial contents; a Gemini CLI OAuth flow that was interrupted before project selection; scripted credential provisioning omitting the field.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/57fa71a1dcc804b1. Report an issue: GitHub.