{"record":{"id":"136049c382b9ee1b","repo":"Billionmail/BillionMail","slug":"failed-to-initialize-ai-client","errorCode":null,"errorMessage":"failed to initialize AI client","messagePattern":"failed to initialize AI client","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/chat.go","lineNumber":279,"sourceCode":"\tif err != nil {\n\t\treturn errors.New(\"Chat not found\")\n\t}\n\t// set the chat information\n\tchatInfo.SupplierName = supplierName\n\tchatInfo.ModelId = modelId\n\terr = SaveChat(chatId, chatInfo)\n\tif err != nil {\n\t\treturn errors.New(\"Failed to save chat\")\n\t}\n\n\tChatStatus[chatId] = true // Set chat status to active\n\tdefer func() {\n\t\tdelete(ChatStatus, chatId) // Ensure chat status is removed after processing\n\t}()\n\n\taiObj := NewOpenAI(ctx, supplierInfo.ApiKey, supplierInfo.BaseUrl, modelId, chatId, supplierName, modelInfo.MaxTokens)\n\tif aiObj == nil {\n\t\treturn errors.New(\"failed to initialize AI client\")\n\t}\n\taiObj.GetClient()\n\treturn aiObj.Chat(content, isText)\n}\n\n// RemoveChat removes a chat by its ID\n// This function should handle the logic for removing a chat based on the provided chat ID\nfunc RemoveChat(chatId string) error {\n\tchatPath := CHAT_CONFIG_PATH + \"/\" + chatId\n\tif !public.FileExists(chatPath) {\n\t\treturn os.ErrNotExist\n\t}\n\terr := os.RemoveAll(chatPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/chat.go#L261-L297","documentation":"Chat constructs the OpenAI-compatible client with NewOpenAI and returns this error if the constructor returns nil. A nil client means required configuration (API key, base URL, model metadata) was insufficient to build the client, so the request cannot proceed.","triggerScenarios":"Calling Chat when supplierInfo.ApiKey is empty, BaseUrl is malformed/empty, or modelInfo.MaxTokens is invalid such that NewOpenAI returns nil.","commonSituations":"Supplier saved without an API key; BaseUrl pointing at a wrong or unreachable proxy URL; model metadata missing MaxTokens after a partial model import.","solutions":["Verify supplierInfo.ApiKey is set for the supplier (re-enter the API key in settings)","Verify supplierInfo.BaseUrl is a valid reachable URL (e.g., https://api.openai.com/v1)","Check modelInfo.MaxTokens is a sane positive value for the chosen model","Inspect NewOpenAI to see which condition returns nil and add logging there"],"exampleFix":"// before\nerr := askai.Chat(ctx, chatId, supplier, modelId, content, true) // opaque nil-client error\n// after\nsup, _ := askai.GetSupplierConfig(supplier)\nif sup.ApiKey == \"\" || sup.BaseUrl == \"\" {\n    return errors.New(\"supplier api key/base url must be configured\")\n}\nerr = askai.Chat(ctx, chatId, supplier, modelId, content, true)","handlingStrategy":"validation","validationCode":"sup, err := askai.GetSupplierConfig(supplierName)\nif err != nil || sup.ApiKey == \"\" || sup.BaseUrl == \"\" {\n    return errors.New(\"supplier api key and base url must be set\")\n}\nif m := askai.GetModelInfo(supplierName, modelId); m == nil || m.MaxTokens <= 0 {\n    return errors.New(\"model metadata (max tokens) missing\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Require API key + base URL at supplier save time (fail fast in settings UI)","Sanity-check MaxTokens when importing models","Log constructor inputs inside NewOpenAI so nil returns are diagnosable"],"tags":["askai","ai-client","configuration","initialization"],"backgroundTag":"ai-client-init-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}