commit 66110b8a55aafa0248d5facbf46fad9e65b8c242 equwal <truex@equwal.com> 2026-09-26 19:14:38 -0700 Answer 502 when the Google token server fails for a short time google-auth raises a RefreshError that it marks retryable when the Google token server answers 408, 429, 500, 503 or 504 after its own retries. play._call changed each RefreshError to the answer 503 "Google Play purchases are not set up on this server." It also logged "Google refused the Play key" at the ERROR level. So during a short Google failure, a buyer who paid saw that the server was not set up, and the log blamed the key. A retryable RefreshError now gives 502 "Google Play did not answer. Try again later." and no error in the log. A RefreshError that is not retryable still gives 503. The test of the 502 answers now includes a retryable RefreshError. It also checks the text of each answer, and that the log has no error.
backend/play.py | 4 ++++ tests/test_play.py | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/backend/play.py b/backend/play.py index e8ef6ca..b32bf8b 100644 --- a/backend/play.py +++ b/backend/play.py @@ -142,6 +142,10 @@ def _call(method: str, url: str): with _session() as http: response = http.request(method, url, timeout=15) except exceptions.RefreshError as exc: + # A retryable error is a temporary failure of the Google token server, + # for example 503 after the retries of google-auth. The key can be good. + if exc.retryable: + raise PlayError(502, NO_ANSWER) from exc log.error("Google refused the Play key: %s", type(exc).__name__) raise PlayError(503, NOT_SET_UP) from exc except (requests.RequestException, exceptions.TransportError) as exc: diff --git a/tests/test_play.py b/tests/test_play.py index ebdace5..230fa2e 100644 --- a/tests/test_play.py +++ b/tests/test_play.py @@ -472,12 +472,17 @@ def test_google_errors_become_clear_answers(http, google_status, status): requests.ConnectionError("no route to host"), requests.Timeout("read timed out"), google_errors.TransportError("no route to the token server"), + # google-auth raises this after its own retries, when the Google token + # server answers 408, 429, 500, 503 or 504. + google_errors.RefreshError("temporarily_unavailable", retryable=True), ]) -def test_no_connection_to_google_is_502(http, error): +def test_no_connection_to_google_is_502(http, error, caplog): http.answers = [error] with pytest.raises(play.PlayError) as caught: play.get("captions20", "abc") - assert caught.value.status == 502 + assert (caught.value.status, caught.value.detail) == (502, play.NO_ANSWER) + # The key can be good. So the log must not tell the operator that Google refused it. + assert [r for r in caplog.records if r.levelno >= logging.ERROR] == [] def test_a_key_that_google_refuses_is_503(http):