OpenHands / OpenHands/enterprise

[Bug]: V1 git pagination drops one item at every page boundary; search endpoints advertise next pages that duplicate or 400

Open
#73 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
4
Forks
2
Avg merge
1d 22h
Merged PRs (30d)
101

Description

Summary

The V1 git endpoints in openhands/app_server/git/git_router.py detect "is there a next page?" by requesting per_page = limit + 1 from the provider and checking whether more than limit items came back. That trick is only valid for offset-based pagination. The provider services implement page-number pagination (page/per_page are forwarded to the Git provider's API), so requesting limit + 1 items per page shifts every page window by one item — and one item is silently lost at every page boundary.

Affected endpoints

1. GET /api/v1/git/repositories/search without query (user repo listing) — data loss

git_router.py lines 183–196: the router calls client.get_repositories(..., page=page, per_page=limit + 1, ...), truncates the extra item, and emits next_page_id = page + 1.

Example with limit=30 against GitHub (openhands/app_server/integrations/github/service/repos.py:92 forwards page/per_page directly to /user/repos):

  • Page 1 → ?per_page=31&page=1 → repos 1–31, shows repos 1–30
  • Page 2 → ?per_page=31&page=2 → repos 32–62

Repo 31 is never shown. The frontend repo picker (frontend/src/hooks/query/use-git-repositories.ts) infinite-scrolls via next_page_id, so users with many repositories simply cannot find every ~limit-th repository in the dropdown.

2. GET /api/v1/git/branches/search with empty query (branch listing) — same data loss

Same limit + 1 pattern (lines 263–274), so one branch disappears at every page boundary — even though ProviderHandler.get_branches() already returns PaginatedBranchesResponse.has_next_page, which the router ignores.

3. GET /api/v1/git/repositories/search with query — infinite duplicate pages

page is decoded from page_id but never passed to client.search_repositories() (lines 156–171), yet the response still advertises a next_page_id whenever more than limit results come back. Following it returns the same first page again, forever — in an infinite-scroll UI this renders as endlessly repeating search results.

4. GET /api/v1/git/branches/search with query — advertises a page that 400s

Branch search rejects page != 1 with HTTP 400 (the OpenHands/enterprise#22 TODO), but still returns a non-null next_page_id when more than limit matches exist — advertising a next page that is guaranteed to fail when requested.

The existing test suite encodes the data loss

tests/unit/app_server/test_git_router.py::TestSearchRepositories::test_pagination_works_across_pages mocks provider data faithfully to page-number semantics (limit=2, so per_page=3; page 1 → repos 1,2,3; page 2 → repos 4,5,6) and asserts page 1 shows repos 1,2 and page 2 shows repos 4,5 — i.e. the test asserts that repo 3 (and repo 6) are never shown to the user.

Proposed fix (minimal, router-only)
  • Listing paths: request per_page = limit (not limit + 1).
    • Branches: derive next_page_id from the has_next_page flag the providers already return.
    • Repos: emit next_page_id when a full page came back (len(repos) >= limit) — worst case is one trailing empty page, never lost data.
  • Search-with-query paths: stop advertising a next_page_id that cannot be honored (return null) until provider-level search pagination lands (#13883 tracks branch search; repository search needs the equivalent provider work). For consistency with branch search, reject an explicit non-first page_id on repository query search with 400 instead of silently returning duplicate data.

I have a PR ready for this and will link it here.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in openhands/app_server/git/git_router.py at the repository and branch search pagination paths, then inspect the provider pagination behavior and frontend/src/hooks/query/use-git-repositories.ts. Run tests/unit/app_server/test_git_router.py::TestSearchRepositories::test_pagination_works_across_pages and update coverage for listing and query searches. Done means page boundaries no longer drop items, duplicate results, or advertise failing pages.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, github, python
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.