Skip to content

fix(policy-bot): Retry GitHub 502 responses when loading policy#7

Open
wrannaman wants to merge 3 commits into
usemotion:usemotion-patchesfrom
wrannaman:fix/retry-github-502-on-policy-fetch
Open

fix(policy-bot): Retry GitHub 502 responses when loading policy#7
wrannaman wants to merge 3 commits into
usemotion:usemotion-patchesfrom
wrannaman:fix/retry-github-502-on-policy-fetch

Conversation

@wrannaman

Copy link
Copy Markdown

What

isServerError treats only 500, 503 and 504 as retryable. api.github.com intermittently returns 502 on GET /repos/{owner}/{repo}/contents/.policy.yml, so a single 502 skips the existing 3x backoff loop, becomes a LoadError, and posts a red Error loading policy from <org>/<repo>@main status. httpcache also evicts the cached entry on any non-200, so the next request cannot fall back to the last known good policy.

This adds http.StatusBadGateway to the retryable set.

Evidence

From the running deployment (policy-bot-7cb7f8659-ml7tp, mgmt cluster), six load errors in 100 minutes, every one a 502, each self-healing on the next webhook:

time (UTC) repo event
21:03:39 backend-net check_run
21:29:38 backend-net issue_comment
21:34:43 backend-net check_run
21:38:43 internal-tool-camp check_run
21:40:21 backend-net workflow_run
22:06:44 internal-tool-camp check_run
failed to read file: GET https://api.github.com/repos/wonderlydotcom/backend-net/contents/.policy.yml?ref=main: 502  []
  appconfig.getFileContents appconfig.go:281
  handler.(*ConfigFetcher).ConfigForRepositoryBranch fetcher.go:61

A merge_group receives a single checks_requested delivery. A 502 there leaves the status red permanently and boots the PR from the merge queue.

Why not also 429

Deliberately omitted. go-github converts 429 and rate-limited 403 into *RateLimitError / *AbuseRateLimitError (github.go:1536-1548), neither of which satisfies errors.As(err, &ghErr) for *github.ErrorResponse. Adding StatusTooManyRequests to that switch would be unreachable code.

Tests

  • TestIsServerError — table over 500/502/503/504 (retryable) and 403/404/422/non-GitHub errors (not).
  • TestConfigFetcherRetriesBadGateway — loader returns 502 once, then succeeds; asserts two calls and no LoadError. Fails on usemotion-patches without this change, with the exact production error (502 []).

Also fixes a stale expectation in TestParseConfigPostsStatusForSeenPolicy, which still asserted the policy-bot: main status context after this branch dropped the branch suffix. It was already failing on usemotion-patches before this change; go test ./server/handler/... and go vet ./server/... are now clean.

api.github.com intermittently returns 502 on
GET /repos/{owner}/{repo}/contents/.policy.yml. isServerError only
treated 500, 503 and 504 as retryable, so a single 502 skipped the
backoff loop entirely, surfaced as a LoadError, and posted a red
"Error loading policy from <org>/<repo>@main" status. The underlying
httpcache entry is also evicted on any non-200, so the next request
could not fall back to the last known good policy.

Observed on the wonderlydotcom installation: six load errors in 100
minutes across backend-net and internal-tool-camp, every one a 502,
each self-healing on the next webhook. A merge_group receives a single
checks_requested delivery, so a 502 there leaves the status red and
boots the PR from the queue.

429 is deliberately not added: go-github converts 429 and rate-limited
403 into *RateLimitError / *AbuseRateLimitError, which never satisfy
errors.As(err, &ghErr), so such a case would be unreachable.

Also corrects a stale expectation in TestParseConfigPostsStatusForSeenPolicy,
which still asserted the "policy-bot: main" status context after this
branch dropped the branch suffix. It was failing before this change.
wrannaman added 2 commits July 9, 2026 16:17
golangci-lint (unused) fails Verify on this branch: policy/approval/approve_test.go:34:6.
The helper has zero references repo-wide and was orphaned by 2872525.
The Dist job's comment says these steps should only run when publishing,
but the guard was lost when Docker Hub was swapped for GCR. secrets.GCR_JSON_KEY
is not exposed to pull_request events, so docker/login-action fails with
'Password required' on every PR. Dist has failed on all PRs since 2026-04-02,
including PRs opened from usemotion-patches itself.

Upstream guards the equivalent steps with
'github.event_name == push || github.event_name == release'. Excluding only
pull_request preserves the current push and release behaviour.
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