ListDeployments returns unbounded filtered pages with an unstable cursor
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.4k
- Forks
- 364
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 84
Description
What happened:
WebAPI.ListDeployments can return more than one logical page of deployments when label filtering is used. The handler paginates in the datastore layer, applies label filtering in memory, and keeps appending matches from subsequent datastore pages until the scan stops. That means one response can grow beyond the requested PageSize while still returning only the last datastore cursor.
Summary
ListDeployments does not enforce a stable filtered page size after applying label filters.
Affected Area
pkg/app/server/grpcapi/web_api.goWebAPI.ListDeployments
Steps to Reproduce
- Arrange multiple
deploymentStore.Listpages where several deployments match the requestedlabels. - Call
WebAPI.ListDeploymentswith those label filters and aPageSizesmaller than the total number of matching deployments. - Observe that the response accumulates matching deployments across datastore pages into a single
Deploymentsarray. - Observe that the returned cursor is only the final datastore cursor rather than a cursor aligned to the filtered response boundary.
Actual Behaviour
One response can contain more than the intended logical page size when label filtering is applied, and the cursor does not describe a stable resume point for filtered pagination.
Technical Evidence
- The current implementation filters the first datastore page in memory and, if fewer than
PageSizeitems remain, continues fetching more datastore pages and appending every matching deployment. - The handler currently returns
filteredwithout truncating it toPageSizeand includes a TODO noting thatListDeploymentscan exceed the page size. - Relevant code:
pkg/app/server/grpcapi/web_api.goaround theListDeploymentslabel-filter loop.
Suspected Root Cause
Filtering happens after datastore pagination, but the filtered result set does not have its own page boundary or resume cursor.
Proposed Direction
- Keep fetching datastore pages until the filtered response reaches the requested logical page size.
- Stop at the exact filtered page boundary instead of returning every matched deployment gathered during the scan.
- Return a cursor that resumes from the correct datastore position for the next filtered page.
- Add regression tests that cover dense and sparse label matches across multiple datastore pages.
Acceptance Criteria
ListDeploymentsnever returns more than the requested logical page size when label filters are used.- A follow-up request using the returned cursor yields the next filtered page without duplicates or gaps.
- Regression tests cover multi-page filtered pagination.
What you expected to happen:
ListDeployments should honor a stable filtered page size and return a cursor that resumes exactly after the last item included in the filtered response.
How to reproduce it:
- Arrange or mock multiple
deploymentStore.Listpages containing deployments with matching labels. - Call
WebAPI.ListDeploymentswith label filters and aPageSizesmaller than the total filtered match count. - Inspect the response and verify that it currently returns more than one logical page of matches.
Environment:
pipedversion: Not applicablecontrol-planeversion:masteras of July 22, 2026- Others: Reproducible from the current
pkg/app/server/grpcapi/web_api.goimplementation and a focused unit test.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pkg/app/server/grpcapi/web_api.go at WebAPI.ListDeployments and the label-filter loop, then run or inspect the focused unit tests for this handler. Add regression coverage for dense and sparse matches across datastore pages; done means filtered responses respect PageSize and follow-up requests resume without duplicates or gaps.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 70/100