Skip to content

Show how long Apple asked to wait after a refused sign-in - #200

Merged
parawanderer merged 3 commits into
mainfrom
feat/show-apples-retry-after
Sep 13, 2026
Merged

parawanderer merged 3 commits into
mainfrom
feat/show-apples-retry-after

Conversation

@parawanderer

Copy link
Copy Markdown
Owner

Reads Retry-After on a refused sign-in and shows the wait, in the app and the exporter. When the header is absent, nothing changes.

No refusal from Grand Slam has been observed carrying Retry-After, including the 503s and the 429 seen on 2026-09-13. So the case users actually hit is the one without it, and that path invents no number.

FindMy.py fork, bcb0787 (pinned in all four places)

  • AppleServiceUnavailableError.retry_after: seconds from Retry-After, in either RFC 9110 form, looked up case-insensitively because HTTP/2 lowercases header names. None when absent or unreadable.
  • Both refusal sites log the request headers, with X-Apple-I-MD, X-Apple-I-MD-M, the identity token and the GS token removed, plus the response headers in full.
  • The message no longer says the refusal "usually clears on its own".

Exporter

  • describe_apple_declining names the wait, rounded up, and drops "try again shortly" when Apple gave one.

App

  • loginSync sends retryAfterSeconds only when present; PythonAccountLoginException.getRetryAfterSeconds() is null otherwise.
  • The sign-in screen shows login_failed_apple_asked_to_wait with the wait formatted by android.icu.text.MeasureFormat for the locale, which is available from API 24, the app's minSdk. It's a new string in all ten locales.
  • Rounding: seconds below a minute, minutes below two hours, hours after that, always rounded up. The app and the exporter use the same cut-offs.

Tests

Where Present Absent
fork test_service_unavailable.py seconds, HTTP date, past date, lowercase name missing, garbage, negative; secrets redacted in the log
test_apple_asked_to_wait.py reaches Java as retryAfterSeconds, including 0 no key rather than 0
test_apple_declining.py wait named, rounding table no wait named
HowLongToWaitTest (JVM) rounding table n/a
AppleLoginFlowTest aWaitAppleNamedIsShownRoundedUp existing appleDecliningIsNotBlamedOnThePasswordOrTheNetwork

The fork tests were checked by breaking the code: turning off the redaction, making the lookup case-sensitive, and ignoring the header each turned the matching tests red. Java compilation, the JVM suite and the emulator suite run only in CI here.

Not covered: the iCloud list's APPLE_DECLINED path (icloud_bridge) still shows its fixed sentence and doesn't carry the wait.

🤖 Generated with Claude Code

parawanderer and others added 3 commits September 13, 2026 20:49
AppleServiceUnavailableError gains retry_after (None when Apple did not say, which is
every refusal seen so far), and a refusal now logs the request headers minus the secrets
and the response headers in full. Its message no longer promises the refusal clears on
its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A refusal carrying Retry-After now names the wait, rounded up, in place of "try again
shortly". Without the header - every refusal observed so far - the message is unchanged
and names no number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bridge carries retryAfterSeconds only when Apple sent Retry-After, and the screen
replaces "try again shortly" with the wait, rounded up and worded by ICU for the locale.
Absent stays absent all the way across: zero would read as "retry now".

The loginSync failure harness moves to conftest.py so the new bridge tests can drive it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@parawanderer
parawanderer merged commit 029a872 into main Sep 13, 2026
8 checks passed

This branch was successfully deployed

1 active deployment
Android Build — 3c556e3c Deployed Sep 13, 2026 by parawanderer via Instrumented tests (emulator) #254
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