Repository navigation
Description
What happened
When selecting reviewers on a Forgejo pull request, the same 2 reviewers loop over and over in the picker: user-a, user-b, user-a, user-b, ... for the whole list. Selecting one works (every copy of that user shows the check mark), but the list is hundreds of rows long and takes about 8 seconds to load.
Diagnosis
listReviewerCandidates in the Forgejo provider loads /repos/{owner}/{repo}/assignees through the generic page() helper (packages/source-control-forgejo/src/server/ForgejoPullRequestProvider.ts:549). page() keeps asking for ?limit=50&page=N until readPage says there are no more pages, or until it has 500 items (:141-152).
readPage decides whether there is a next page like this (:137):
more: rows.length > 0 && (links === undefined || /rel="?next"?/i.test(links)),So a response with rows and no Link header counts as "there is another page". With fj credentials, ForgejoCli.requestFj writes a link: line only when Forgejo sends one (ForgejoCli.ts:407).
Forgejo's /assignees endpoint is not paginated. GetAssignees (routers/api/v1/repo/collaborators.go in Forgejo) ignores page/limit, returns every assignee in one response and sets no Link header. Every page therefore returns the same full list, more stays true, and the loop only stops at the 500-item cap.
In this case the repo has 3 assignees: 167 requests × 3 = 501 items, truncated: true. After the PR author is filtered out, the picker shows the other 2 users about 167 times each.
The other page() callers (commit statuses, PR commits, reviews, reactions, labels) hit endpoints that do paginate on the Forgejo side, so they end at the first empty page. Only /assignees is affected.
Possible fix direction: read /assignees once (it's a plain unpaginated list, readArray fits), and/or stop treating a missing Link header as "more pages" when the page came back with fewer than limit rows.
Steps to reproduce
- Self-hosted Forgejo (seen on 17.0.0+gitea-1.22.0) with a repo where 2 or more users besides you have write access. T3 Code is signed in to it through an
fjlogin. - In T3 Code, open a pull request in that repo and go to the Summary tab.
- Next to Reviewers, click the add-reviewer button.
- Expected: each assignee once. Actual: after a few seconds, the same users repeat for hundreds of rows. The server trace shows
GET .../assignees?limit=50&page=1throughpage=167for one open of the picker.
Version
0.1.2-nightly.20261022.3512 (desktop app with its local server; main has the same code as of 2026-10-22)
Environment
macOS (Darwin 27.0.0, arm64), T3 Code desktop nightly, Forgejo 17.0.0+gitea-1.22.0 (self-hosted, fj login)
Evidence
# server.trace.ndjson, one opening of the reviewer picker (host and repo redacted)
13:34:48 -> 13:34:56 GET https://forgejo.example.com/api/v1/repos/<owner>/<repo>/assignees?limit=50&page=1 ... page=167
167 requests, all http.response.status_code 200, ~8 s total
13:34:48 ForgejoPullRequestProvider.listReviewerCandidates durationMs=8075 exit=Success
13:34:48 ws.rpc.pullRequests.reviewerCandidates durationMs=8234 exit=Success
# Forgejo GetAssignees (routers/api/v1/repo/collaborators.go): no ListOptions, no SetLinkHeader
assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo().Repository)
...
ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer(), assignees))Related issues
#23551 is also about Forgejo/Gitea API paging cost, but it covers PR lookups by branch, not reviewer candidates. No existing issue covers this.
Fix applied or workaround
Nothing changed on the machine. Workaround: pick the reviewer from the top of the list (selection works), or request reviewers in the Forgejo web UI.
Filed by
Claude Code (claude-opus-5-5) via t3 triage
Activity
[-]Forgejo reviewer picker repeats the same assignees over and over[/-][+]Forgejo and Gitea reviewer picker repeats the same assignees over and over[/+]