{"record":{"id":"ef0a05bbd0408b40","repo":"Billionmail/BillionMail","slug":"failed-to-save-chat","errorCode":null,"errorMessage":"Failed to save chat","messagePattern":"Failed to save chat","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/chat.go","lineNumber":269,"sourceCode":"\t\treturn errors.New(\"Supplier not found\")\n\t}\n\n\tmodelInfo := GetModelInfo(supplierName, modelId)\n\tif modelInfo == nil {\n\t\treturn errors.New(\"Model not found\")\n\t}\n\n\t// Check if the chat is already active\n\tchatInfo, err := GetChat(chatId)\n\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 {","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/chat.go#L251-L287","documentation":"After mutating the loaded chat with the new supplier/model, Chat persists it with SaveChat and returns this error if saving fails, swallowing the underlying cause. It means the chat state could not be written, so the session is not activated.","triggerScenarios":"Calling Chat when SaveChat fails internally — e.g., storage/Redis unavailable, serialization failure of chatInfo, or underlying store write error.","commonSituations":"Redis or backing store down during deployment; disk/memory pressure on the persistence layer; concurrent writes corrupting the chat record causing save rejection.","solutions":["Check the persistence layer (Redis/store) used by SaveChat is healthy","Inspect SaveChat to log/propagate its underlying error instead of masking it","Retry Chat once the store is available; the chat status flag was not set, so state is consistent"],"exampleFix":"// before\nif err := SaveChat(chatId, chatInfo); err != nil {\n    return errors.New(\"Failed to save chat\")\n}\n// after\nif err := SaveChat(chatId, chatInfo); err != nil {\n    return fmt.Errorf(\"failed to save chat %s: %w\", chatId, err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := askai.Chat(ctx, chatId, supplier, model, content, true); err != nil {\n    if err.Error() == \"Failed to save chat\" {\n        // check store health (redis/db) then retry with backoff\n        return retryable(err)\n    }\n    return err\n}","preventionTips":["Monitor the backing store used by SaveChat","Improve the library to wrap the underlying save error with %w","Avoid concurrent writes to the same chatId"],"tags":["askai","persistence","chat","state"],"backgroundTag":"state-save-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}