diff --git a/internal/bridge/user_events.go b/internal/bridge/user_events.go index c0ee71bd..923bd8e5 100644 --- a/internal/bridge/user_events.go +++ b/internal/bridge/user_events.go @@ -56,6 +56,9 @@ func (bridge *Bridge) handleUserEvent(ctx context.Context, user *user.User, even case events.UserBadEvent: bridge.handleUserBadEvent(ctx, user, event.Error) + + case events.UncategorizedEventError: + bridge.handleUncategorizedErrorEvent(event) } return nil @@ -139,7 +142,8 @@ func (bridge *Bridge) handleUserDeauth(ctx context.Context, user *user.User) { func (bridge *Bridge) handleUserBadEvent(ctx context.Context, user *user.User, err error) { safe.Lock(func() { if rerr := bridge.reporter.ReportMessageWithContext("Failed to handle event", reporter.Context{ - "error": err, + "error_type": fmt.Sprintf("%T", err), + "error": err, }); rerr != nil { logrus.WithError(rerr).Error("Failed to report failed event handling") } @@ -147,3 +151,12 @@ func (bridge *Bridge) handleUserBadEvent(ctx context.Context, user *user.User, e bridge.logoutUser(ctx, user, true, false) }, bridge.usersLock) } + +func (bridge *Bridge) handleUncategorizedErrorEvent(event events.UncategorizedEventError) { + if rerr := bridge.reporter.ReportMessageWithContext("Failed to handle due to uncategorized error", reporter.Context{ + "error_type": fmt.Sprintf("%T", event.Error), + "error": event.Error, + }); rerr != nil { + logrus.WithError(rerr).Error("Failed to report failed event handling") + } +} diff --git a/internal/events/user.go b/internal/events/user.go index a2a03188..d94bb5b7 100644 --- a/internal/events/user.go +++ b/internal/events/user.go @@ -179,3 +179,14 @@ type IMAPLoginFailed struct { func (event IMAPLoginFailed) String() string { return fmt.Sprintf("IMAPLoginFailed: Username: %s", event.Username) } + +type UncategorizedEventError struct { + eventBase + + UserID string + Error error +} + +func (event UncategorizedEventError) String() string { + return fmt.Sprintf("UncategorizedEventError: UserID: %s, Source:%T, Error: %s", event.UserID, event.Error, event.Error) +} diff --git a/internal/user/user.go b/internal/user/user.go index 65580dcf..7c58bff7 100644 --- a/internal/user/user.go +++ b/internal/user/user.go @@ -20,9 +20,11 @@ package user import ( "context" "crypto/subtle" + "encoding/json" "errors" "fmt" "io" + "net" "strings" "sync/atomic" "time" @@ -649,6 +651,24 @@ func (user *User) doEventPoll(ctx context.Context) error { return fmt.Errorf("failed to handle event due to network issue: %w", err) } + // Catch all for uncategorized net errors that may slip through. + if netErr := new(net.OpError); errors.As(err, &netErr) { + user.eventCh.Enqueue(events.UncategorizedEventError{ + UserID: user.ID(), + Error: err, + }) + + return fmt.Errorf("failed to handle event due to network issues (uncategorized): %w", err) + } + + // In case a json decode error slips through. + if jsonErr := new(json.UnmarshalTypeError); errors.As(err, &jsonErr) { + user.eventCh.Enqueue(events.UncategorizedEventError{ + UserID: user.ID(), + Error: err, + }) + } + // If the error is a server-side issue, return error to retry later. if apiErr := new(proton.APIError); errors.As(err, &apiErr) && apiErr.Status >= 500 { return fmt.Errorf("failed to handle event due to server error: %w", err)