Skip to content

fix(github): a rate-limited workflow approval read no longer shows a check awaiting action - #32990

Open
only21mil wants to merge 2 commits into
pingdotgg:mainfrom
only21mil:fix/github-approval-read-keeps-rate-limit
Open

only21mil wants to merge 2 commits into
pingdotgg:mainfrom
only21mil:fix/github-approval-read-keeps-rate-limit

Conversation

@only21mil

@only21mil only21mil commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When GitHub refuses the workflow-run read for a fork pull request because of a rate limit, T3 adds a "Workflow approval status" check that is awaiting action. Every real check can be green, and the pull request panel shows "1 check awaiting action". This shows up when many watches re-read at once and GitHub answers part of the burst with a secondary rate limit or a host pause.

Change

listWorkflowRunsRequiringApproval reads in two halves. The GraphQL half already lets rate-limit errors through; the REST half wrapped every failure as a read error, rate limits and host pauses included. GitHubPullRequestProvider only passes GitHubApiRateLimitError and SourceControlRateLimitPausedError through and turns every other failure into the "could not determine" placeholder, so a refused read became that placeholder. The REST half now keeps those two errors as they are; every other failure still becomes a read error and still shows the placeholder. A rate-limited read now fails as rate-limited, the same as a rate-limited detail read, and the panel reports the rate limit.

Scope

This is a bug fix: the panel shows a wrong status, "1 check awaiting action", where GitHub actually refused the read for a rate limit. It is a small fix for an obvious bug, so it has no issue: the provider already handles rate limits on this read, but that branch could never match for the REST half. It changes one error mapping and adds one test; workflow approval, the placeholder for other failures and the approve action's other errors are unchanged.

This is a regression from #16320: before it, this read went through the gh CLI and a rate limit reached the provider's rate-limit branch.

Verification

To see it on Linux web:

  1. Open a passing fork pull request from Pull requests.
  2. Have GitHub refuse its workflow-run read with a secondary rate limit (here a proxy does it, below).
  3. Main shows "1 check awaiting action"; the fix reports the rate limit with Retry.

Before, main 2b07556: 1 check awaiting action, Workflow approval status awaiting action

Before, main 2b07556d71: 1 check awaiting action, Workflow approval status awaiting action

After, head d5411fc: GitHub API rate limit exceeded, with Retry

After, head d5411fcaf4: GitHub API rate limit exceeded, with Retry

Retry then shows the real checks, here and merged with main. Details below.

Re-checked against main 476fc9b on 2026-11-12: the new test fails there without the change and passes with it; the branch merges cleanly. A maintainer's real run on d5411fc covered the normal path (approval reads answered HTTP 200); the refusal path is the one re-checked here.

Not checked: desktop and mobile (same server data), and a refusal GitHub itself sent (the proxy stands in).

Method, test output, Retry, the head merged with main, and the proxy log

Related: #24599 changes how the watch reports workflows really waiting for approval and makes it ignore this placeholder check, so it no longer wakes an agent with "checks failed"; #26561 adds approval for same-repository pull requests.

Method: a T3 test server per revision ("Test machine", fresh state, a read-only GitHub App token) reading real GitHub. In the web client, in T3's Browser panel, I opened this pull request (a fork pull request whose checks all pass) from the Pull requests view. Stand-in: a pass-through proxy between the server and api.github.com forwards every request unchanged except one, the workflow-run read for this head (actions/runs?head_sha=d5411fc…&status=action_required). It answers that read with a secondary rate-limit refusal instead of sending it: HTTP 403 and retry-after: 60, as GitHub's REST rate-limit docs describe, with the message GitHub's own throttling plugin uses ("You have exceeded a secondary rate limit. Please wait a few minutes before you try again."). It is not a refusal GitHub sent. Order: main 2b07556d71, then this head d5411fcaf4, then this head merged locally with that main (0ffb47cbb4, not pushed). For Retry, the proxy was switched to pass that read through and I waited out the 60 seconds.

After Retry with the read passing, head d5411fc: 24 of 31 passing, no Workflow approval status row

After Retry with the read passing, head d5411fcaf4: 24 of 31 passing, no Workflow approval status row

After, head merged with main 0ffb47cbb4: GitHub API rate limit exceeded, with Retry

After, head merged with main 0ffb47cbb4: GitHub API rate limit exceeded, with Retry

After Retry with the read passing, head merged with main 0ffb47cbb4: 24 of 31 passing, no Workflow approval status row

After Retry with the read passing, head merged with main 0ffb47cbb4: 24 of 31 passing, no Workflow approval status row

  • New test in packages/source-control-github/src/server/GitHubPullRequestApi.test.ts, "keeps $_tag when the workflow run read is rate-limited", one row per error, run on the merged tree: with main's GitHubPullRequestApi.ts both rows fail; with the fix all 155 tests in the API and provider files pass.
  • vp run check:changed 2b07556d71 on the merged tree, Node 26.12.1: format, lint, graphql, knip, typecheck (11 packages) and tests pass.

Test before, on the merged tree with main's GitHubPullRequestApi.ts:

$ node --version
v26.12.1
$ git checkout 2b07556d71 -- packages/source-control-github/src/server/GitHubPullRequestApi.ts
$ vp test run src/server/GitHubPullRequestApi.test.ts -t 'workflow run read is rate-limited' 2>&1 | grep -E '×|^AssertionError|Tests '
     × keeps GitHubApiRateLimitError when the workflow run read is rate-limited 37ms
     × keeps SourceControlRateLimitPausedError when the workflow run read is rate-limited 3ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
AssertionError: expected 'GitHubPullRequestReadError' to equal 'GitHubApiRateLimitError'
AssertionError: expected 'GitHubPullRequestReadError' to equal 'SourceControlRateLimitPausedError'
      Tests  2 failed | 123 skipped (125)

Test after, with the fix:

$ node --version
v26.12.1
$ vp test run --reporter=verbose src/server/GitHubPullRequestApi.test.ts src/server/GitHubPullRequestProvider.test.ts 2>&1 | grep -E 'workflow run read is rate-limited|Test Files|Tests '
 ✓ src/server/GitHubPullRequestApi.test.ts > GitHubPullRequestApi.layer > keeps GitHubApiRateLimitError when the workflow run read is rate-limited 2ms
 ✓ src/server/GitHubPullRequestApi.test.ts > GitHubPullRequestApi.layer > keeps SourceControlRateLimitPausedError when the workflow run read is rate-limited 1ms
 Test Files  2 passed (2)
      Tests  155 passed (155)

Proxy log, main 2b07556d71 (the refused read, then a second workflow-run read for this head, passed through):

$ grep -F 'actions/runs?head_sha=d5411fca' proxy-main.jsonl
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&branch=fix%2Fgithub-approval-read-keeps-rate-limit&event=pull_request&status=action_required&per_page=100&page=1", "status": 403, "refused_by_proxy": true, "at": "2026-11-12T08:18:33Z"}
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&per_page=100", "status": 200, "refused_by_proxy": false, "at": "2026-11-12T08:18:34Z"}

Proxy log, head d5411fcaf4 (refused, then passed through on Retry):

$ grep -F 'actions/runs?head_sha=d5411fca' proxy-head.jsonl
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&branch=fix%2Fgithub-approval-read-keeps-rate-limit&event=pull_request&status=action_required&per_page=100&page=1", "status": 403, "refused_by_proxy": true, "at": "2026-11-12T08:21:30Z"}
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&branch=fix%2Fgithub-approval-read-keeps-rate-limit&event=pull_request&status=action_required&per_page=100&page=1", "status": 200, "refused_by_proxy": false, "at": "2026-11-12T08:22:42Z"}

Proxy log, head merged with main 0ffb47cbb4 (refused, then passed through on Retry):

$ grep -F 'actions/runs?head_sha=d5411fca' proxy-merged.jsonl
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&branch=fix%2Fgithub-approval-read-keeps-rate-limit&event=pull_request&status=action_required&per_page=100&page=1", "status": 403, "refused_by_proxy": true, "at": "2026-11-12T08:24:27Z"}
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&branch=fix%2Fgithub-approval-read-keeps-rate-limit&event=pull_request&status=action_required&per_page=100&page=1", "status": 200, "refused_by_proxy": false, "at": "2026-11-12T08:25:40Z"}
{"method": "GET", "path": "/repos/pingdotgg/t3code/actions/runs?head_sha=d5411fcaf488fd96192079808e385040951c28c8&per_page=100", "status": 200, "refused_by_proxy": false, "at": "2026-11-12T08:25:41Z"}

Generated with Claude Opus 5.5 in T3 Code Nightly

…action-required

The REST half of listWorkflowRunsRequiringApproval wrapped every failure,
rate limits and host pauses included, as a read error. The provider only
passes rate limits through, so a paused read became the "Workflow approval
status" action-required placeholder and woke PR watches on green heads.
Rate-limit and pause errors now keep their tags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:unvouched Author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 8, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at d5411fc

Macroscope's review found this PR approvable — Rate-limited workflow-approval reads now retain their rate-limit status instead of appearing to require action. The change uses existing handling without altering approval permissions or workflow execution.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The workflow approval read now preserves GitHub API rate-limit errors and paused source-control rate-limit errors. Other errors continue to be converted to GitHubPullRequestReadError. A parameterized test covers both rate-limit error types.

Changes: Workflow read errors

Layer / File(s) Summary
Rate-limit error propagation and tests
packages/source-control-github/src/server/GitHubPullRequestApi.ts, packages/source-control-github/src/server/GitHubPullRequestApi.test.ts
The workflow-run read preserves both rate-limit error types and continues converting other errors to GitHubPullRequestReadError. A parameterized test checks that each rate-limit error retains its original tag.

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix

Suggested reviewers: bil0000, tonybynmp4

Merge Risk: Minimal · up to 019da

A rate-limited workflow approval read now surfaces as a rate-limit error instead of appearing as a check awaiting action. Other failures behave as before. No actionable merge risk remains.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the fix in the GitHub workflow-approval read: a rate-limited read no longer shows a 'Workflow approval status' check as awaiting action. The raw summary says the REST half of listWorkf…
Description check Passed The description follows the template. It has Problem, Change, Scope, and Verification sections. The Problem and Change sections clearly explain the bug and the fix. Scope links no issue but gives a re…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the fix in the GitHub workflow-approval read: a rate-limited read no longer shows a 'Workflow approval status' check as awaiting action. The raw summary says the REST half of listWorkflowRunsRequiringApproval now keeps GitHubApiRateLimitError and SourceControlRateLimitPausedError unchanged, which is the main change. The title is specific and matches the changeset, and it follows a conventional commit format.

Full details: Description check

Explanation

The description follows the template. It has Problem, Change, Scope, and Verification sections. The Problem and Change sections clearly explain the bug and the fix. Scope links no issue but gives a reason for that, as the template allows for small obvious fixes. Verification is detailed and reports focused tests, a proxy-based reproduction, before/after results, and what was not checked. Related PRs and a note on the regression are included. The template's agent-model line is present (Claude Opus 5.5 in T3 Code Nightly). The description is long, with an extended details block, but it covers every required section.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between d5411fc and 019daed.

Files selected for processing: 2

  • packages/source-control-github/src/server/GitHubPullRequestApi.test.ts
  • packages/source-control-github/src/server/GitHubPullRequestApi.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

@maria-rcks

maria-rcks commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

partial verification of neighboring behavior at d5411fcaf488fd96192079808e385040951c28c8 on linux. i ran github/main (7171fdd771) in a separate worktree, stopped it, then ran the exact pr head with fresh isolated state. no mocks or injected responses were used.

i created a real repository project and threads through the web app and completed real codex gpt-6.1-sol turns. playwright with /usr/bin/google-chrome opened this fork pr through the app. full detail refresh and the checks popover worked: 24 checks passed and 7 were skipped. watch registration showed Waiting · Watching pull request #32990; stop watching removed it.

server traces confirm the workflow approval rest read reached github and returned http 200 on both revisions. runtime transcript.

blacksmith-check: 148 focused api/provider tests passed. vp run check:changed ade473018d32003658b7d38693e083ee2e314bd8 passed format, lint, knip, graphql, affected typechecks, and tests. no code changes or pushes.

live pr head showing the real checks popover

live watch and stop watching controls, detail refresh, and checks popover

Not verified

the core rate-limit fix remains unverified against real github. main did not reproduce the original bug: all observed workflow approval reads returned http 200 with quota remaining. neither a rate-limit refusal nor a host pause occurred, so i could not prove the pr's refusal propagation or the watcher skipping that sweep. the successful reads and tests above do not prove that case.

the pr's codex session reported its pull-request read tool unavailable, so agent-side pr reading was not verified. native desktop and mobile clients were not exercised on this linux vm.

@only21mil

Copy link
Copy Markdown
Contributor Author

Note

🤖 Opus 5.5 replying on behalf of Victor.

Thanks for running this in a real dev environment. Your run shows the change leaves the normal path alone. With real workflow approval reads returning HTTP 200, the checks popover, detail refresh, and watch and stop watching behave on d5411fc the same way they do on main. The 148 focused tests and check:changed also match ours.

You're right that the refusal path itself is still unproven against real GitHub. Main didn't reproduce it for you because every approval read had quota left, and the bug only shows when GitHub refuses part of a burst. The PR's evidence for that path is the test that fails on the base and passes with the fix, plus the loopback stub that answers with GitHub's secondary rate-limit 403. The description lists a live sweep with the fix as not checked. The agent-side pull request read tool being unavailable is outside this change, which only touches one error mapping in the REST read.

@juliusmarminge juliusmarminge added the takeover:in-progress A takeover lead is working on this PR label Nov 15, 2026
@juliusmarminge

Copy link
Copy Markdown
Member

Evidence

Open Pull requests, search #32990, and open its detail panel while a proxy returns a rate-limit 403 only for the action_required workflow-approval REST read. Both sides use fresh isolated state, Chromium at 1280×800, and light theme. scenarios:prove exited 0.

Before After
4117bd42ec 019daeded5
Incorrectly shows '1 check awaiting action', without Retry. Shows 'GitHub API rate limit exceeded.' with Retry.
Before: incorrect awaiting-action check After: rate-limit error with Retry
Before recording After recording

Scenario source · Proxy driver

Not shown:

  • The separately paused-host error path was not exercised; this proof covers the workflow-approval REST rate-limit response.
  • Repeated refresh, clicking Retry, and recovery after removing the refusal were not exercised.

@juliusmarminge juliusmarminge added takeover:needs-julius Takeover done; route gate says Julius decides and removed takeover:in-progress A takeover lead is working on this PR labels Nov 15, 2026

This branch has not been deployed

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

Labels

size:S 10-29 changed lines (additions + deletions). takeover:needs-julius Takeover done; route gate says Julius decides vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants