slopus/happy · warning
[Gemini] No API key found. Run 'happy connect gemini' to aut
Error message
[Gemini] No API key found. Run 'happy connect gemini' to authenticate via Google OAuth, or set ${GEMINI_API_KEY_ENV} environment variable. What it means
createGeminiBackend resolves a Gemini API key from (in order) happy auth storage, local ~/.gemini config, GEMINI_API_KEY, GOOGLE_API_KEY, and an explicit apiKey option. If none is present it logs this warning; the backend is still created but gemini will likely fail to authenticate when launched.
Source
Thrown at packages/happy-cli/src/agent/factories/gemini.ts:92
export function createGeminiBackend(options: GeminiBackendOptions): GeminiBackendResult {
// Resolve API key from multiple sources (in priority order):
// 1. Happy cloud OAuth token (via 'happy connect gemini') - highest priority
// 2. Local Gemini CLI config files (~/.gemini/)
// 3. GEMINI_API_KEY environment variable
// 4. GOOGLE_API_KEY environment variable - lowest priority
// Try reading from local Gemini CLI config (token and model)
const localConfig = readGeminiLocalConfig();
let apiKey = options.cloudToken // 1. Happy cloud token (passed from runGemini)
|| localConfig.token // 2. Local config (~/.gemini/)
|| process.env[GEMINI_API_KEY_ENV] // 3. GEMINI_API_KEY env var
|| process.env[GOOGLE_API_KEY_ENV] // 4. GOOGLE_API_KEY env var
|| options.apiKey; // 5. Explicit apiKey option (fallback)
if (!apiKey) {
logger.warn(`[Gemini] No API key found. Run 'happy connect gemini' to authenticate via Google OAuth, or set ${GEMINI_API_KEY_ENV} environment variable.`);
}
// Command to run gemini
const geminiCommand = 'gemini';
// Get model from options, local config, system environment, or use default
// Priority: options.model (if provided) > local config > env var > default
// If options.model is undefined, check local config, then env, then use default
// If options.model is explicitly null, skip local config and use env/default
const model = determineGeminiModel(options.model, localConfig);
// Build args - use only --experimental-acp flag
// Model is passed via GEMINI_MODEL env var (gemini CLI reads it automatically)
// We don't use --model flag to avoid potential stdout conflicts with ACP protocol
const geminiArgs = ['--experimental-acp'];
// Get Google Cloud Project from local config (for Workspace accounts)
// Only use if: no email stored (global), or email matches current userView on GitHub (pinned to b824cd0a46)
Solutions
- Run `happy connect gemini` to authenticate via Google OAuth.
- Or export GEMINI_API_KEY=<your-key> (or GOOGLE_API_KEY) in the environment where happy runs.
- If using the daemon, set the env var in the daemon's environment and restart it, since daemons don't inherit your shell env.
- Verify with `echo $GEMINI_API_KEY` that the variable is set and non-empty in the actual execution environment.
Example fix
// before happy gemini // warn: no API key // after export GEMINI_API_KEY=AIza... happy gemini
Defensive patterns
Strategy: validation
Validate before calling
if (!process.env.GEMINI_API_KEY && !process.env.GOOGLE_API_KEY && !fs.existsSync(require('os').homedir() + '/.gemini/oauth_creds.json')) {
console.error('No Gemini credentials: run `happy connect gemini` or export GEMINI_API_KEY.');
process.exit(1);
} Prevention
- Run `happy connect gemini` once on each machine.
- Export GEMINI_API_KEY in shell profile AND in the daemon environment.
- In CI, inject the key as a secret env var.
- For daemons, use an EnvironmentFile so the key survives restarts.
When it happens
Trigger: Running happy with the gemini agent (happy gemini) before ever running 'happy connect gemini', with no GEMINI_API_KEY/GOOGLE_API_KEY env var set in the current (or daemon) environment, and no apiKey in options.
Common situations: Fresh machine after cloning dotfiles without the key, CI/daemon environment that lacks the interactive shell's exported GEMINI_API_KEY, or key stored in ~/.gemini that was wiped/reinstalled.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Authentication failed
- Token exchange failed: ${tokenResponse.statusText}
- Invalid JWT format
- Token exchange failed: ${error}
- Token exchange failed: ${error}
AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31).
Data as JSON: /api/errors/495dd60268f55015.
Report an issue: GitHub.