{"record":{"id":"c1df052eed4d8e14","repo":"Billionmail/BillionMail","slug":"chat-not-found-or-already-stopped","errorCode":null,"errorMessage":"Chat not found or already stopped","messagePattern":"Chat not found or already stopped","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/service/askai/chat.go","lineNumber":303,"sourceCode":"// 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}\n\nfunc Stop(chatId string) error {\n\t// Implementation for stopping a chat\n\t// This function should handle the logic for stopping a chat based on the provided parameters\n\tif _, exists := ChatStatus[chatId]; !exists {\n\t\treturn errors.New(\"Chat not found or already stopped\")\n\t}\n\tChatStatus[chatId] = false // Set chat status to inactive\n\treturn nil\n}\n\n// GetLastUsage retrieves the last usage statistics for a chat by its ID\n// This function should handle the logic for loading the last usage statistics of a chat based on the provided chat ID\nfunc GetLastUsage(chatId string) ChatUsage {\n\tmessages := GetMessages(chatId)\n\tif len(messages) == 0 {\n\t\treturn ChatUsage{}\n\t}\n\tlastMessage := messages[len(messages)-1]\n\treturn lastMessage.Usage\n}\n\n// GetHtml retrieves the HTML content of a chat by its ID\n// This function should handle the logic for loading the HTML content of a chat based on the provided chat ID","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/chat.go#L285-L321","documentation":"Stop() looks up the chat ID in the in-process ChatStatus map; if the ID is absent it means the chat was never started (or its entry was removed) or it was already stopped. The library throws this sentinel error to reject stopping a non-active chat instead of silently mutating the map.","triggerScenarios":"Calling askai.Stop(chatId) with an ID that was never registered in ChatStatus (chat never started), an ID that was already stopped (entry removed on completion), a stale ID after process restart (ChatStatus is in-memory only), or a typo'd/foreign chat ID.","commonSituations":"Double-clicking a 'stop' button so Stop fires twice; a frontend retrying Stop after the stream already finished; a server restart wiping the in-memory map while clients still hold old chat IDs; load-balanced deployments where the chat lives on another instance.","solutions":["Check chat existence first (e.g. a ChatExists helper or reading ChatStatus) before calling Stop","Treat this error as an idempotent success in the caller: the desired end state (chat stopped) is already reached","Ensure the same instance that started the chat handles Stop (sticky sessions/shared state) since ChatStatus is process-local","Verify the chatId passed is the exact ID returned when the chat was created"],"exampleFix":"// before\nif err := askai.Stop(chatId); err != nil {\n    return err\n}\n// after\nif err := askai.Stop(chatId); err != nil {\n    if err.Error() == \"Chat not found or already stopped\" {\n        return nil // already stopped; idempotent\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"func chatIsActive(chatId string) bool {\n    askai.ChatStatusMu.Lock()\n    defer askai.ChatStatusMu.Unlock()\n    active, ok := askai.ChatStatus[chatId]\n    return ok && active\n}\n// call Stop only if chatIsActive(chatId)","typeGuard":null,"tryCatchPattern":"if err := askai.Stop(chatId); err != nil {\n    if strings.Contains(err.Error(), \"not found or already stopped\") {\n        return nil // idempotent: already in desired state\n    }\n    return err\n}","preventionTips":["Track chat IDs client-side and disable the stop control once a stream completes","Treat Stop as idempotent — never surface this error to end users","Remember ChatStatus is in-memory; use sticky routing or shared state across replicas","Never reuse or guess chat IDs; always use the ID returned at chat creation"],"tags":["go","state-management","chat","idempotency"],"backgroundTag":"resource-not-found","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"}