{"record":{"id":"2d9dcd3e3293fc02","repo":"wavetermdev/waveterm","slug":"ai-endpoint-is-required-2d9dcd","errorCode":null,"errorMessage":"ai:endpoint is required","messagePattern":"ai:endpoint is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/gemini/gemini-backend.go","lineNumber":68,"sourceCode":"\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}\n\n\t// Map thinking level for Gemini 3+ models\n\tif opts.ThinkingLevel != \"\" && strings.Contains(opts.Model, \"gemini-3\") {","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/gemini/gemini-backend.go#L50-L86","documentation":"buildGeminiHTTPRequest requires a non-empty endpoint to know where to POST the Gemini request. 'ai:endpoint is required' is thrown when chatOpts.Config.Endpoint is empty; unlike some clients, this library does not fall back to the default Gemini base URL for you.","triggerScenarios":"RunGeminiChatStep is called with chatOpts.Config.Endpoint == \"\": the endpoint was never configured for the Gemini backend, was cleared, or provider-specific endpoint setup was skipped when constructing WaveChatOpts.","commonSituations":"Custom endpoint configured for another provider but not Gemini; a config reset wiped the endpoint; users behind a proxy/gateway forgot to set the Gemini endpoint; code paths that build opts manually and omit Endpoint.","solutions":["Set the ai:endpoint setting for the Gemini backend to the Gemini API base URL (or your proxy/gateway URL)","If your code constructs WaveChatOpts programmatically, assign opts.Endpoint explicitly","Check for config that sets endpoint under a different provider key and move/duplicate it for Gemini","If a default is desired, set it at the call site before invoking RunGeminiChatStep rather than relying on the library"],"exampleFix":"// before\nopts := uctypes.WaveChatOpts{Config: uctypes.WaveChatOptsConfig{Model: \"gemini-2.0-flash\", APIToken: key}}\n// after\nopts := uctypes.WaveChatOpts{Config: uctypes.WaveChatOptsConfig{Model: \"gemini-2.0-flash\", APIToken: key, Endpoint: \"https://generativelanguage.googleapis.com\"}}","handlingStrategy":"validation","validationCode":"if chatOpts.Config.Endpoint == \"\" {\n    chatOpts.Config.Endpoint = \"https://generativelanguage.googleapis.com\"\n}","typeGuard":null,"tryCatchPattern":"if _, _, _, err := RunGeminiChatStep(ctx, handler, chatOpts, cont); err != nil {\n    if err.Error() == \"ai:endpoint is required\" {\n        chatOpts.Config.Endpoint = \"https://generativelanguage.googleapis.com\"\n        return retry()\n    }\n    return err\n}","preventionTips":["Set the endpoint whenever you configure a Gemini token/model — treat the trio as one unit","When switching providers, copy endpoint configuration explicitly rather than assuming defaults","Validate endpoint URL format at config load time"],"tags":["gemini","configuration","endpoint"],"backgroundTag":"missing-config-value","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}