Repository navigation
fix(calendar): stop retrying refused grants and failed push channels - #667
Merged
Merged
Conversation
A token endpoint's invalid_grant, interaction_required, consent_required or login_required now marks the account reconnect required, read from the OAuth error code rather than its description. Such accounts leave the push sweep and the reconcile after a sync, and their channel rows are dropped until they reconnect. A failed push channel now stamps its attempt time, so a repeated failure still backs off: 6 hours after a 4xx, 30 minutes after a network error, 5xx or 429. The log and last_error keep the HTTP status and the provider's error code and message. Fixes: #661 Fixes: #662
This was referenced Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Description
Fixes #661 and #662. Both sit in the calendar sync's failure handling and share
calendar_push_service.py.#661.
_get_access_token_for_connectionnow reads the token endpoint's OAutherrorcode. A 400 or 401 withinvalid_grant,interaction_required,consent_requiredorlogin_requiredraisesCalendarReauthorisationRequired, which sync records asreauthorisation_requiredwith the reconnect wording Settings already shows. Google's "Bad Request" and "Token has been expired or revoked." bodies and Microsoft's AADSTS refusals all carry one of those codes. A reconnect-required account is left out of the push sweep's provisioning list, and the reconcile (after a sync, or from the sweep) asks for no token: it drops the account's channel rows and returns. The next failed attempt reclassifies an existing row, so there is no migration.#662.
_mark_channel_failedstampsupdated_atitself, so writing the same failure again still moves the backoff clock.last_errorand the warning now hold the HTTP status and the provider's code and message, capped at 200 characters: Graph'serror.codeanderror.message, Google'serrors[].reasonanderror.message. Only the response body's error fields are read, never the request, so no token or calendar address gets in. A 4xx other than 429 waits 6 hours. A network error, 5xx or 429 waits 30 minutes, as before.docs/CALENDAR.mddescribes both, adds the CDN or WAF line to "Live Sync Not Activating", and notes that a Google app in testing mode ends each sign-in after seven days.Decision record
_get_access_token_for_connectionreplaces the old match on "reauthorisation" in aValueError's text._classify_sync_failurealso reads the OAuth code on a raw token-endpoint error, as a backstop.invalid_clientis not a refusal. A bad client secret is the admin's to fix and reconnecting would not help, so a 400invalid_clientstays "Needs attention" and keeps retrying. A 401 still means reconnect, as before.HTTP <status>that startslast_error. The alternatives were aretry_aftercolumn, which means a migration for a best-effort feature, or a new channel status, a wider change in meaning. A row in the old format gets the short wait once and is then rewritten.Type of change
Checks run
scripts/check.pygates (lint, format, blindexcept, heldpins, typecheck, docs, alembic) and the full pytest suite with Postgres, 3651 passed. Host side: whitespace, file size, docs validator,git diff --check.The new tests use Google's two
invalid_grantbodies and Microsoft AADSTS bodies, and drive a refused grant throughsync_connection_by_idon SQLite to check the token endpoint is asked once. The backoff tests write the same Graph refusal again and again through the real ORM and check that the attempts land at 0, 6 and 12 hours. All of them fail onmain.Migration impact
Documentation impact
docs/CALENDAR.md: Sync Behaviour, Enabling Live Sync, Google Testing Mode Blocks a User, Live Sync Not Activating.Security impact
Manual verification
None by hand beyond the tests.