Skip to content

COR-255 - Report a login refused as temporarily unavailable as its own error - #122

Merged
dbezverkhnii merged 1 commit into
spark2from
fix/COR-255-unavailable-login-refusal
Oct 22, 2026
Merged

dbezverkhnii merged 1 commit into
spark2from
fix/COR-255-unavailable-login-refusal

Conversation

@dbezverkhnii

@dbezverkhnii dbezverkhnii commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

A server can refuse a login with the RFC 5530 response code UNAVAILABLE — a temporary failure on its side, not the credentials. Yahoo answers NNN NO [UNAVAILABLE] AUTHENTICATE Server error - Please try again later about nine seconds after the command (30% of Yahoo mailboxes a day in production, 89% of the episodes clear on the next attempt), iCloud answers BAD [UNAVAILABLE] Service temporarily unavailable, Dovecot servers (iiNet, the report in COR-255) NO [UNAVAILABLE] Account is temporarily unavailable.. No login text matched, so mailcore reported ErrorAuthentication and Spark treated the refusal as a wrong password: auth-failed mark, password prompt, forced OAuth refresh, rebuild of every pool and a retry every ~9 s — against a server that, per production data, keeps refusing longer when a client keeps knocking.

  • IMAPSession::login reads the RFC 5530 code of the reply libetpan parsed (imap_response_info->rsp_atom, rebuilt on every parsed reply) after MAILIMAP_ERROR_LOGIN or MAILIMAP_ERROR_PROTOCOL (the BAD form) and reports the new ErrorIMAPUnavailable, with mShouldDisconnect so that the next login starts on a fresh connection. The parsed field, not the text: libetpan strips the bracketed code out of imap_response, so a text match would never see it, and the [LIMIT] check reads the raw stream buffer only because libetpan stops at * BYE before parsing the tagged line.
  • The check sits after the connection-limit texts: NO [UNAVAILABLE] Maximum number of connections … stays ErrorGmailTooManySimultaneousConnections, the more specific answer with the pool narrowing it calls for.
  • The code is appended after ErrorIMAPTooManySimultaneousConnections (50) in the C++, C and Swift enums; like it, no ObjC constant and no localized description. iCloud's BAD [UNAVAILABLE] moves from ErrorConnection to the new code; Spark maps both to its connection error.

Spark's side (mapping, the pause before the next attempt, discovery, health counter) is the core PR for COR-255; it needs this change released and the pin bumped.

The C++ changed, so the Windows prebuilt needs a rebuild for this revision before a tag.

Tests: IMAPLoginTests against LeaseTestTCPEndpoint, each refusal for LOGIN, XOAUTH2, SASL PLAIN and SASL LOGIN. NO [UNAVAILABLE] gave 5 and BAD [UNAVAILABLE] gave 1 before the fix; [UNAVAILABLE] with the limit text, wrong credentials, Gmail's text and a bare BYE keep their codes. The wire shape matches the user logs attached to COR-255 and the Mac/iOS support logs: one tagged line, no * BYE, no continuation.

🤖 Generated with Claude Code


Note

Medium Risk
Changes IMAP login error classification and appends a new enum value; downstream clients must handle the new code, but scope is limited to login failure mapping.

Overview
Adds ErrorIMAPUnavailable across the C/C++/Swift error enums so IMAP login failures with RFC 5530 [UNAVAILABLE] are not reported as ErrorAuthentication.

IMAPSession::login now reads the parsed response code from libetpan (imap_response_info->rsp_atom) when login returns MAILIMAP_ERROR_LOGIN or MAILIMAP_ERROR_PROTOCOL (e.g. iCloud’s BAD [UNAVAILABLE]). It maps UNAVAILABLE to the new error and sets mShouldDisconnect so the next attempt uses a fresh connection. Connection-limit message checks still run first, so NO [UNAVAILABLE] with “Maximum number of connections” text stays ErrorGmailTooManySimultaneousConnections.

IMAPLoginTests cover LOGIN, XOAUTH2, SASL PLAIN, and SASL LOGIN, including NO / BAD [UNAVAILABLE], precedence over limit text, and unchanged behavior for wrong credentials and other refusals.

Reviewed by Cursor Bugbot for commit db12d3c. Bugbot is set up for automated code reviews on this repo. Configure here.

…OR-255

A server can refuse a login with the RFC 5530 code `UNAVAILABLE`: Yahoo
answers `NO [UNAVAILABLE] AUTHENTICATE Server error - Please try again
later` about nine seconds after the command, iCloud `BAD [UNAVAILABLE]
Service temporarily unavailable`, Dovecot servers in their own words.
No text matched, so the login reported `ErrorAuthentication`, and the
client treated a temporary failure as a wrong password: auth-failed
mark, password prompt, forced token refresh, pool rebuild and a retry
every few seconds against a server that was refusing for another reason.

`IMAPSession::login` now reads the code of the parsed reply
(`imap_response_info->rsp_atom`, rebuilt by libetpan on every parsed
reply) after `MAILIMAP_ERROR_LOGIN` or `MAILIMAP_ERROR_PROTOCOL` (BAD)
and reports the new `ErrorIMAPUnavailable`, with `mShouldDisconnect` so
the next login starts on a fresh connection. The check comes after the
connection-limit texts: a refusal that names the limit stays the limit.
The code is appended after `ErrorIMAPTooManySimultaneousConnections` in
the C++, C and Swift enums; like it, it has no ObjC constant and no
localized description.

Tests: `IMAPLoginTests` against `LeaseTestTCPEndpoint`, for LOGIN,
XOAUTH2, SASL PLAIN and SASL LOGIN. `NO [UNAVAILABLE]` gave 5 and
`BAD [UNAVAILABLE]` gave 1 before the fix; `[UNAVAILABLE]` with the
limit text, wrong credentials and a bare BYE keep their codes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dbezverkhnii

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit db12d3c. Configure here.

@dbezverkhnii
dbezverkhnii marked this pull request as ready for review October 22, 2026 13:02
@dbezverkhnii
dbezverkhnii merged commit 5c15e80 into spark2 Oct 22, 2026
9 of 10 checks passed
@dbezverkhnii
dbezverkhnii deleted the fix/COR-255-unavailable-login-refusal branch October 22, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant