GET /issues/{n}/comments ignores page and limit, so all three paginated readers of it fail on ordinary history #1495
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit#1495
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The issue-comments endpoint on this forge does not paginate. It ignores both
pageandlimit, and every request returns the complete list. Three readers in the tree walk it as if it paginated, and each one fails once the history is non-empty:scripts/record-landing-review.shgo-ci / record reviewed vs landed commit (push)is red on main and no record is postedforgejo.ListIssueComments(paginateStrict)MaxPages=40rt prep's rolling-deadlock path is fatal (latent)forknotice.findExistingMeasured
Source. Forgejo tag
v15.0.7on Codeberg,routers/api/v1/repo/issue_comment.go, functionListIssueComments:sinceandbefore.issues_model.FindCommentsOptions{IssueID, Since, Before, Type}with noListOptions.models/issues/comment.goFindCommentspaginates onlyif opts.Page > 0, so it never does here.X-Total-Count.Live.
GET /api/v1/versionreturns15.0.7+gitea-1.22.0. Probed 2026-10-02:limitis ignored too:limit=1still returns 2 rows.default_paging_num=30(GET /settings/api)./reviewspaginates normally.Not measured live: a history above
max_response_items=50. No issue or PR in this repo has more than 42 comments (a scan of all 1494), and I did not post 50 comments to make one. The source reading above is the evidence that a single read is not capped at 50.1. Visible now: the landing recorder is red on main
go-ci / record reviewed vs landed commit (push), read from each job's log in the web UI:2e8f46fcomment page 2 repeated the previous non-empty page; endpoint did not advance; refusing to grade, thenexitcode '2'29b541bexitcode '2'55a12b9Every PR merged since the bounding change (#1472,
c5142ab) follows the same split:112396) and #1472 (112387).The green control is
f639dcb. It was a direct push:/commits/{sha}/pullreturns 404, so the script exits before it reads any comments.Not this bug:
31677ccalso shows this job asfailure, but on the(pull_request)context, and the job in run 18451 reads Canceled.#1469 turned the endless walk into this refusal. Its AC accepted
rc=2at the repeated comment page. It kept paginating, though, so the recorder now refuses on every landed PR that has a comment.2. Latent:
rt prep's rolling-deadlock path is fatal on any commentHow the failure reaches
rt prep:internal/forgejo/reads.goListIssueCommentscallspaginateStrict, which callspaginateMode(internal/forgejo/client.go).paginateModeends only on an empty page. Otherwise it returns ErrAPIpagination hit MaxPages=40 cap on a full page.internal/prep/pr.goAnnounceRollingPRDeadlockreads comments to de-duplicate its marker and returns any read error.cmd/rt/prep.goreturns that error, sort prepfails.Measured on unfixed
origin/main29b541b, ingolang:1.25, against a fake server that ignorespagethe way Forgejo does:So:
It hasn't surfaced because the path runs only while a rolling PR carries a live
REQUEST_CHANGES. The existing fixtures serve[]for comments, or answer page 2 with[], which is not how this endpoint behaves.3. Latent: the fork-PR approval notice at 50+ comments
internal/forknoticefindExistingwalks?limit=50&page=Nand stops on a page shorter than 50 rows. Below 50 comments, page 1 is "short" and the walk ends correctly. At 50 or more, measured against the same page-ignoring fake on29b541b:found more than one approval noticeafter 2 GETs.comment walk exceeded 100 pagesafter 100 GETs.Why it matters
maingets a redgo-cicontext on almost every PR landing. The reviewed-vs-landed identity record (#1299) has not been posted for any commented PR since #1472. A context that is always red also teaches people to ignore it.rt prep: the deadlock notice exists so that a blocked rolling PR is visibly blocked (#1183). As written, it makesrt prepfatal on the cut path instead.Acceptance criteria
record-landing-review.shreads the comments collection in ONE request, with nopageorlimit. It validates the response as a JSON array of{id: number, body: string}rows and searches the full list for the marker. The reviews walk stays paginated and bounded. — DONE @ #1496 (e4c0f35, reviewed ase4c0f35). One bare GET inscripts/record-landing-review.sh; the reviews walk keepscheck_page_progressandmax_pages.forgejo.ListIssueCommentsmakes ONE request and strict-decodes a JSON array, so{},nulland a row withoutidall refuse. The error classes stay the same. — DONE @ #1496 (e4c0f35, reviewed ase4c0f35).callRetry+decodeJSONArray, as inListDirectory; the{},nulland missing-idarms refuse.forknotice.findExistingmakes ONE request, and its duplicate-notice refusal stays. — DONE @ #1496 (e4c0f35, reviewed ase4c0f35). Duplicate-notice refusal kept; JSONnullnow refused too./issues/{n}/commentsand what happened to each. — DONE @ #1496 (e4c0f35, reviewed ase4c0f35). See the PR description's "Every GET reader of the endpoint" table: 3 readers, plus the POST-only and prose hits.pagethe way Forgejo does, returning the same non-empty array on every request. Each fixture asserts one request and the full list. Each arm is mutation-verified: it goes red when the paginating read is restored. — DONE @ #1496 (e4c0f35, reviewed ase4c0f35). Mutants M1–M7 are red (PR description), plus the reviewer's two (review 7179).changelog.d/<issue>-<slug>.fixed.mdfragment is added. — DONE @ #1496 (e4c0f35, reviewed ase4c0f35).changelog.d/1495-comments-single-read.fixed.md; the density check passes.go-ci / record reviewed vs landed commit (push)is green and its PR carries the record (verified by read-back). — DONE @ #1496 (e4c0f35, reviewed ase4c0f35). Run 18971success; its log readsrecorded review-to-landing identity for PR #1496 at e4c0f35…. Record comment 114071 names review 7181 by @alex: "no identity change".Not in scope
X-Total-Countcross-check: comparing the single read againstX-Total-Count(live, it equals the row count on all three issues probed). It would catch a future Forgejo that starts paginating this endpoint. The Go client'scalldoes not expose response headers today, so it needs its own tracker.Another instance, read from the job log in the web UI.
2060ed1(#1493, landed 2026-10-02 12:20):go-ci / record reviewed vs landed commit (push)failed in run 18782, job 2, withcomment page 2 repeated the previous non-empty page; endpoint did not advance; refusing to grade, thenexitcode '2'.The other 15 push contexts on that commit are green, so the red
go-cionmainthere is this recorder alone. The same context is also red on55a12b9(#1485) and29b541b(#1484), at "Failing after 3s" per the commit status, which is consistent with the table above. I did not read those two logs.Another instance, read from the job log in the web UI.
de49186(#1508, landed 2026-10-02 13:23):go-ci / record reviewed vs landed commit (push)failed in run 18842, job 2, withcomment page 2 repeated the previous non-empty page; endpoint did not advance; refusing to grade, thenexitcode '2'.When I read the commit status, 14 of the other 15 push contexts on that commit were green, and
release / fire-cut (push)was still waiting to run. So the redgo-cionmainthere is this recorder alone. The PR had two comments and three reviews when it landed.All seven acceptance criteria are ticked above. The PR used
Refs, so this issue is being shut by hand.mainat 15:07 ase4c0f35f0abfa39486089f0be4ec3a00ee53a9fd.merge_commit_shaequals it, and a localgit range-diffshows=against the original commit.e4c0f35, official and not stale. The reviewer's verdicts are reviews 7179 and 7180, posted as COMMENT with "Verdict: APPROVE ate4c0f35".e4c0f35:go-ci / record reviewed vs landed commit (push), run 18971. Its log reads "recorded review-to-landing identity for PR #1496", and record comment 114071 on #1496 reports "no identity change".release / fire-cut (push)is queued for agorunner. In the same run,rt decidechosemode=update, sofire-cutwill skip, as it did on the earlier commits. No release is being cut.pull_requestcontext was green at merge.The
X-Total-Countcross-check and back-filling the missed records remain out of scope, as listed above.