Skip to content

Grand Slam still returns a sporadic 429, on a fresh connection #226

Description

@parawanderer

The connection-reuse fix removed the deterministic 429. It did not remove all of them. One turned up in the app while testing the 1.1.0 build: press sign in, 429, press sign in again, straight through.

The deterministic case is real and still live at Apple

Six unauthenticated POSTs to gsa.apple.com/grandslam/GsService2 with a junk body, down one reused connection:

pooled (one reused connection): [404, 429, 429, 429, 429, 429]

The shipped path avoids that, and still sees the occasional refusal

The same six requests through FindMy.py's HttpSession, which routes gsa.apple.com through a force_close connector:

unpooled (HttpSession, shipped): [404, 404, 404, 404, 429, 404]

So the residual one is not connection reuse — every request there is a new connection. It clears on the next attempt, which is why pressing the button again works, and it is the argument for the app retrying a 429 once itself rather than presenting it as a failure. A user who does not know to press the button again reads it as the app being broken, which is how #206 arrived.

What is not established

Whether spacing the requests helps. A second run measured 1/8 refused back to back against 7/8 at 1.5-second intervals:

 back to back: [404, 404, 404, 404, 404, 429, 404, 404]  -> 1/8
   1.5s apart: [429, 429, 429, 404, 429, 429, 429, 429]  -> 7/8

That reads as spacing making it worse, and it should not be believed. The spaced arm ran second, after roughly fourteen requests had already gone out from the same address, so a rolling per-address budget explains the numbers at least as well as spacing does. The experiment has to be run in the other order, from an address that has been quiet, before either conclusion is worth anything.

One thing that is worth knowing before reading any result here: anything probing this shares an address budget with anything else signing in from the same network. A phone on the same WiFi as the machine running the probe is drawing on the same allowance, so a 429 on the phone during a probe run says nothing about the app.

Reproducing it

No account and no credentials — the edge decides on headers and connection state, so a junk body is enough. Same approach as scripts/check_gsa_edge.py.

import asyncio
from aiohttp import ClientSession, ClientTimeout, TCPConnector
from findmy.util.http import HttpSession

URL = "https://gsa.apple.com/grandslam/GsService2"
HEADERS = {
    "Content-Type": "text/x-xml-plist",
    "Accept": "*/*",
    "User-Agent": "akd/1.0 CFNetwork/1494.0.7 Darwin/23.4.0",
    "X-MMe-Client-Info":
        "<MacBookPro18,3> <macOS;13.4.1;22F82> <com.apple.AuthKit/1 (com.apple.akd/1.0)>",
}

async def pooled(n=6):
    async with ClientSession(connector=TCPConnector(limit=1),
                             timeout=ClientTimeout(total=20)) as s:
        out = []
        for _ in range(n):
            async with s.post(URL, data=b"probe", headers=HEADERS) as r:
                out.append(r.status)
        return out

async def unpooled(n=6):
    s, out = HttpSession(), []
    try:
        for _ in range(n):
            out.append((await s.post(URL, data=b"probe", headers=HEADERS)).status_code)
    finally:
        await s.close()
    return out

print("pooled  ", asyncio.run(pooled()))
print("unpooled", asyncio.run(unpooled()))

Interactively co-authored by Claude Code and @parawanderer

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    @appIssues regarding the OpenTagViewer Android app@exporter-toolIssues regarding the desktop export tool (wizard and CLI)bugSomething isn't workinghelp wantedExtra attention is needed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions