{"record":{"id":"73c39554d7f0a572","repo":"MHSanaei/3x-ui","slug":"telegram-api-test-failed-w","errorCode":null,"errorMessage":"telegram API test failed: %w","messagePattern":"telegram API test failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/web.go","lineNumber":626,"sourceCode":"\t\t\t})\n\t\t}\n\t}\n\n\t// Register email subscriber (always — it checks smtpEnable at runtime)\n\temailService := email.NewEmailService(s.settingService)\n\temailSub := email.NewSubscriber(s.settingService, emailService)\n\ts.bus.Subscribe(\"email-notifier\", emailSub.HandleEvent)\n\n\t// Wire email service to controller for test endpoint\n\tcontroller.SetEmailService(emailService)\n\n\t// Wire Telegram test function to controller\n\tcontroller.SetTestTgFunc(func() error {\n\t\tif !s.tgbotService.IsRunning() {\n\t\t\treturn fmt.Errorf(\"telegram bot is not running (check token and chat ID)\")\n\t\t}\n\t\tif err := s.tgbotService.TestConnection(); err != nil {\n\t\t\treturn fmt.Errorf(\"telegram API test failed: %w\", err)\n\t\t}\n\t\ts.tgbotService.SendMsgToTgbotAdmins(\"✅ Test message from 3x-ui\")\n\t\treturn nil\n\t})\n\n\tcontroller.SetReloadTgbotFunc(func() {\n\t\tenabled, err := s.settingService.GetTgbotEnabled()\n\t\tif err != nil || !enabled {\n\t\t\tif s.tgbotService.IsRunning() {\n\t\t\t\ts.tgbotService.Stop()\n\t\t\t}\n\t\t\tif s.bus != nil {\n\t\t\t\ts.bus.Unsubscribe(\"tg-notifier\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\t// Start() stops any previous receiver first, so it is safe whether or not the bot is already running.\n\t\ttgBot := s.tgbotService.NewTgbot()","sourceCodeStart":608,"sourceCodeEnd":644,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/web.go#L608-L644","documentation":"Wrapper produced inside the same SetTestTgFunc closure when TestConnection() fails while the bot IS running. The %w chain carries the underlying cause: almost always 'bot not initialized' (handle lost, e.g. bot stopped between the IsRunning check and the call) or 'API unreachable: ...' (network/401 to api.telegram.org).","triggerScenarios":"Race where the bot is stopped/reloaded between the IsRunning() check and TestConnection(); or a running-but-broken bot state (token was valid at start, revoked since; server lost egress to Telegram).","commonSituations":"Revoked/regenerated tokens while the bot was live; intermittent egress to api.telegram.org; concurrent settings save reloading the bot during a test.","solutions":["Read the tail of the wrapped error to pick the real cause, then apply the fix for that leaf error (401 -> re-paste token; network -> fix egress; 'not initialized' -> reload bot).","Retry the test once after re-saving Telegram settings (save triggers a reload).","If it recurs on every save, check that the tgbot reload path (SetReloadTgbotFunc) is completing without error in the logs."],"exampleFix":"// before\nif err := s.tgbotService.TestConnection(); err != nil {\n    return fmt.Errorf(\"telegram API test failed: %w\", err)\n}\n\n// after — surface the leaf cause directly since the wrapper adds no information\nif err := s.tgbotService.TestConnection(); err != nil {\n    return fmt.Errorf(\"telegram test failed: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"if err := testTg(); err != nil {\n    unwrapped := errors.Unwrap(err) // skip the 'telegram API test failed' layer\n    if unwrapped != nil && strings.Contains(unwrapped.Error(), \"bot not initialized\") {\n        // reload bot; else fix network/token per leaf error\n    }\n}","preventionTips":["Unwrap %w chains before choosing a remedy — this wrapper's message adds no cause.","Avoid concurrent reload+test; serialize via the settings mutex."],"tags":["telegram","network","wrapper"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}