{"record":{"id":"22358bb821c1db52","repo":"wavetermdev/waveterm","slug":"ai-apitoken-is-required","errorCode":null,"errorMessage":"ai:apitoken is required","messagePattern":"ai:apitoken is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/gemini/gemini-backend.go","lineNumber":65,"sourceCode":"\tfor i := len(contents) - 1; i >= 0; i-- {\n\t\tif contents[i].Role == \"user\" {\n\t\t\tcontents[i].Parts = append(contents[i].Parts, GeminiMessagePart{\n\t\t\t\tText: text,\n\t\t\t})\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// buildGeminiHTTPRequest creates an HTTP request for the Gemini API\nfunc buildGeminiHTTPRequest(ctx context.Context, contents []GeminiContent, chatOpts uctypes.WaveChatOpts) (*http.Request, error) {\n\topts := chatOpts.Config\n\n\tif opts.Model == \"\" {\n\t\treturn nil, errors.New(\"ai:model is required\")\n\t}\n\tif opts.APIToken == \"\" {\n\t\treturn nil, errors.New(\"ai:apitoken is required\")\n\t}\n\tif opts.Endpoint == \"\" {\n\t\treturn nil, errors.New(\"ai:endpoint is required\")\n\t}\n\n\tmaxTokens := opts.MaxTokens\n\tif maxTokens <= 0 {\n\t\tmaxTokens = GeminiDefaultMaxTokens\n\t}\n\n\t// Build request body\n\treqBody := &GeminiRequest{\n\t\tContents: contents,\n\t\tGenerationConfig: &GeminiGenerationConfig{\n\t\t\tMaxOutputTokens: int32(maxTokens),\n\t\t\tTemperature:     0.7, // Default temperature\n\t\t},\n\t}","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/gemini/gemini-backend.go#L47-L83","documentation":"buildGeminiHTTPRequest requires an API token to authenticate Gemini requests. 'ai:apitoken is required' is thrown when chatOpts.Config.APIToken is empty, since the library sends the token as the Gemini API key and cannot call the API anonymously. This is an explicit fail-fast check before any network I/O.","triggerScenarios":"RunGeminiChatStep is called with chatOpts.Config.APIToken == \"\": no API key was configured, the key was stored under a different provider's token field, or env/config loading failed silently leaving the token unset.","commonSituations":"User never obtained a Google AI Studio API key; key was set for OpenAI/Anthropic but not Gemini; the config file was overwritten or a migration dropped the token; running in an environment where the secret store/env var is unavailable.","solutions":["Set the ai:apitoken setting to a valid Google AI Studio API key for the Gemini provider","Confirm the token is configured for the Gemini backend specifically (each provider has its own token field)","Verify config/env loading actually populated opts.APIToken before calling RunGeminiChatStep","Re-check the key after rotating/regenerating it in Google AI Studio and restarting the app"],"exampleFix":"// before\nchatOpts.Config.APIToken = os.Getenv(\"\") // unset\n// after\nif chatOpts.Config.APIToken == \"\" {\n    chatOpts.Config.APIToken = os.Getenv(\"GEMINI_API_KEY\")\n}","handlingStrategy":"validation","validationCode":"if chatOpts.Config.APIToken == \"\" {\n    return fmt.Errorf(\"Gemini API token not configured; set ai:apitoken or GEMINI_API_KEY\")\n}","typeGuard":null,"tryCatchPattern":"if _, _, _, err := RunGeminiChatStep(ctx, handler, chatOpts, cont); err != nil {\n    if err.Error() == \"ai:apitoken is required\" {\n        return fmt.Errorf(\"missing Gemini API key: configure it in settings\")\n    }\n    return err\n}","preventionTips":["Store each provider's API key in its own config field — do not reuse the OpenAI/Anthropic token for Gemini","Check token presence at startup, before the first chat, and prompt the user to add it","After rotating keys in Google AI Studio, update the app config and restart"],"tags":["gemini","configuration","api-key","authentication"],"backgroundTag":"missing-api-key","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}