From 888a4d1c2f1cf601cb326002c5150c7f23ca8820 Mon Sep 17 00:00:00 2001 From: highesttt Date: Wed, 14 Oct 2026 20:24:38 -0400 Subject: [PATCH 1/2] login: classify expired email verification --- pkg/connector/loginerrors.go | 4 ++++ pkg/line/errors.go | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/pkg/connector/loginerrors.go b/pkg/connector/loginerrors.go index 16d6df4..1ab9bf7 100644 --- a/pkg/connector/loginerrors.go +++ b/pkg/connector/loginerrors.go @@ -1,9 +1,11 @@ package connector import ( + "errors" "fmt" "net/http" + "github.com/beeper/line/pkg/line" "maunium.net/go/mautrix/bridgev2" ) @@ -53,6 +55,8 @@ func wrapLineLoginError(err error) error { reason = details.ErrorMessage } switch { + case errors.Is(err, line.ErrLoginVerificationExpired): + mapped = ErrLoginVerificationFailed case isBlockedUserLoginError(reason): mapped = ErrLoginTooManyAttempts case details.HTTPStatus == http.StatusTooManyRequests: diff --git a/pkg/line/errors.go b/pkg/line/errors.go index ff30ce4..dae5c2c 100644 --- a/pkg/line/errors.go +++ b/pkg/line/errors.go @@ -12,9 +12,10 @@ import ( ) var ( - ErrNoUsableE2EEPublicKey = errors.New("no usable E2EE public key") - ErrNoUsableE2EEGroupKey = errors.New("no usable E2EE group key") - ErrGroupKeyNotFound = errors.New("group key not found") + ErrLoginVerificationExpired = errors.New("LINE login verification expired") + ErrNoUsableE2EEPublicKey = errors.New("no usable E2EE public key") + ErrNoUsableE2EEGroupKey = errors.New("no usable E2EE group key") + ErrGroupKeyNotFound = errors.New("group key not found") ) type tokenRefreshError struct { @@ -264,6 +265,19 @@ func loginPollingFailure(httpStatus int, body []byte) error { Data talkExceptionData `json:"data"` } decodeErr := json.Unmarshal(body, &response) + if decodeErr == nil && response.Code != nil && *response.Code == 10052 { + var httpError struct { + Data struct { + StatusCode int `json:"statusCode"` + } `json:"data"` + } + if json.Unmarshal(body, &httpError) == nil && httpError.Data.StatusCode == http.StatusGone { + if httpStatus != http.StatusOK { + return fmt.Errorf("LF1 polling failed: API error %d: LINE response code 10052, status 410: %w", httpStatus, ErrLoginVerificationExpired) + } + return fmt.Errorf("LF1 polling failed: LINE response code 10052, status 410: %w", ErrLoginVerificationExpired) + } + } details := "LINE response without a valid code" if decodeErr == nil && response.Code != nil { details = fmt.Sprintf("LINE response code %d", *response.Code) From 96280bf60b48242e0e4de2a5e6c0914a1296e1b6 Mon Sep 17 00:00:00 2001 From: highesttt Date: Wed, 14 Oct 2026 20:44:41 -0400 Subject: [PATCH 2/2] login: fix import grouping --- pkg/connector/loginerrors.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/connector/loginerrors.go b/pkg/connector/loginerrors.go index 1ab9bf7..18760c1 100644 --- a/pkg/connector/loginerrors.go +++ b/pkg/connector/loginerrors.go @@ -5,8 +5,9 @@ import ( "fmt" "net/http" - "github.com/beeper/line/pkg/line" "maunium.net/go/mautrix/bridgev2" + + "github.com/beeper/line/pkg/line" ) // genericLoginFailureReason is shown in the login form when LINE rejects the sign-in but