{"record":{"id":"de36e618a749d9ab","repo":"Billionmail/BillionMail","slug":"reconnect-failed-w","errorCode":null,"errorMessage":"reconnect failed: %w","messagePattern":"reconnect failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/sending.go","lineNumber":340,"sourceCode":"\t\te.mutex.Lock()\n\t}\n\n\t// Try to send the message, with reconnect on failure\n\terr := e.doSend(message, recipients)\n\tif err != nil {\n\t\t// Connection might be stale, try to reconnect once\n\t\tg.Log().Debug(context.Background(), \"SMTP send failed, attempting reconnection\")\n\n\t\t// Clean up\n\t\te.client.Close()\n\t\te.connected = false\n\t\te.client = nil\n\n\t\t// Try to reconnect\n\t\te.mutex.Unlock()\n\t\tif err := e.Connect(); err != nil {\n\t\t\te.mutex.Lock()\n\t\t\treturn fmt.Errorf(\"reconnect failed: %w\", err)\n\t\t}\n\t\te.mutex.Lock()\n\n\t\t// Try sending again after reconnect\n\t\treturn e.doSend(message, recipients)\n\t}\n\n\treturn nil\n}\n\n// doSend performs the actual message sending\nfunc (e *EmailSender) doSend(message Message, recipients []string) error {\n\t// Add default headers if not present\n\tif message.Headers == nil {\n\t\tmessage.Headers = make(map[string]string)\n\t}\n\n\t// Add Message-ID if not already set","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/sending.go#L322-L358","documentation":"After doSend failed (likely a stale connection), Send closes the client, nulls the connection, and retries Connect() once; this error wraps that reconnect failure. It means the fresh full connect (dial/TLS/auth) also failed, so the original send error is superseded by the reconnect error. Check the wrapped chain for the underlying cause.","triggerScenarios":"Send() on an existing connection whose doSend errored (server closed connection, timeout, reset), then the automatic re-connect also fails — network outage, server down, or credentials no longer accepted.","commonSituations":"SMTP server restarted or idle-killed the connection and is temporarily refusing new ones; a network blip spanning both the send failure and reconnect; password rotated between the original connect and reconnect; server rate-limiting repeated connections from the same IP.","solutions":["Unwrap to the underlying Connect error (dial vs TLS vs auth) and fix accordingly — do not assume the original doSend error was a network issue.","Add backoff between the doSend failure and the reconnect attempt to ride out transient outages and avoid rate-limit loops.","Verify credentials are still valid if the reconnect fails specifically at the AUTH stage.","Consider a connection-health check (NOOP) before each send instead of relying on failure-then-reconnect."],"exampleFix":"// before\ne.mutex.Unlock()\nif err := e.Connect(); err != nil {\n    e.mutex.Lock()\n    return fmt.Errorf(\"reconnect failed: %w\", err)\n}\ne.mutex.Lock()\n// after\ne.mutex.Unlock()\ntime.Sleep(time.Second) // brief backoff before reconnect\nvar connErr error\nfor attempt := 0; attempt < 3; attempt++ {\n    if connErr = e.Connect(); connErr == nil {\n        break\n    }\n    time.Sleep(time.Duration(attempt+1) * 2 * time.Second)\n}\ne.mutex.Lock()\nif connErr != nil {\n    return fmt.Errorf(\"reconnect failed: %w\", connErr)\n}","handlingStrategy":"retry","validationCode":"func sendWithGuard(sender *EmailSender, msg Message, rcpts []string) error {\n    err := sender.Send(msg, rcpts)\n    if err != nil && strings.Contains(err.Error(), \"reconnect failed\") {\n        time.Sleep(5 * time.Second) // server likely down: wait before giving up\n        err = sender.Send(msg, rcpts)\n    }\n    return err\n}","typeGuard":"func isReconnectErr(err error) bool {\n    return strings.Contains(err.Error(), \"reconnect failed\")\n}","tryCatchPattern":"if err := sender.Send(msg, rcpts); err != nil {\n    if isReconnectErr(err) {\n        // schedule the message for a later retry queue rather than failing hard\n        enqueueForRetry(msg, rcpts, backoff=1*time.Minute)\n        return nil\n    }\n    return err\n}","preventionTips":["Use bounded exponential backoff for reconnects; a single immediate retry often hits the same outage.","Check server health (port probe) before attempting reconnect to distinguish outage from config errors.","Persist failed messages to a retry queue instead of dropping them on reconnect failure."],"tags":["smtp","reconnect","retry","connection"],"backgroundTag":"smtp-connection-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"}