Skip to content

fix: page backward in auto_paging_iter after a before cursor - #745

Open
tribble wants to merge 1 commit into
mainfrom
fix/page-direction
Open

tribble wants to merge 1 commit into
mainfrom
fix/page-direction

Conversation

@tribble

@tribble tribble commented Oct 3, 2026

Copy link
Copy Markdown

I confirmed that auto_paging_iter() cannot page backward: after a call like list_users(before="u21"), the follow-up request keeps the original before and adds after, and the WorkOS API rejects any request carrying both cursors (Please provide either "after" or "before" parameters.). Backward pagination fails on page two; this fixes it.

  • auto_paging_iter() now follows the direction of the initial request: a page first fetched with before (and no after) keeps paging backward with before; anything else pages forward with after exactly as before.
  • Follow-up requests send only the cursor for that direction — the stale before is dropped going forward, and after is dropped going backward. The caller's params dict is never modified.
  • Iterating backward yields each page's items reversed, so list(list_users(before="u21", limit=10).auto_paging_iter()) returns u20 down to u1.

Test plan

Red-first evidence, recorded against mocked transports that capture every request's query string:

  • On origin/main (9bbc82d), mise x uv -- uv run pytest tests/test_pagination.py -q → 2 failed, 13 passed: the new backward tests (sync + async) fail because items come back in forward order, and a probe of the same path shows the second request going out as ?limit=10&before=u21&order=desc&after=u21 — the both-cursors shape the API rejects.
  • On this branch the same command → 15 passed: backward iteration yields u20..u1 with requests before=u21 then before=u11 and no after in any request; forward iteration yields u1..u100 with after=u10, after=u20, …; the caller's params dict is asserted unchanged after full iteration.

@tribble
tribble requested review from a team as code owners October 3, 2026 06:15
@tribble
tribble requested a review from csrbarber October 3, 2026 06:15
@greptile-apps

greptile-apps Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 3/5

[Medium risk] Fixes pagination direction tracking for backward cursor iteration.

The PR should not merge until backward iteration preserves requested ordering and handles the events pagination metadata.

Findings

  1. P1 Backward pages lose requested order ▶
  2. P1 Events pagination stops early ▶
Fix with agent prompt
### Issue 1
src/workos/_pagination.py:75
When a caller uses `order="normal"` with a `before` cursor, results are documented as descending even though `before` fetches older records. This reversal yields each page in ascending order instead, so sync and async iteration no longer preserve the requested order.

### Issue 2
src/workos/_pagination.py:80
`list_events(before=...)` returns pagination metadata with only an `after` cursor. This new backward branch requires `page.before`, so it reverses the first page and stops without fetching further results. The async iterator has the same behavior.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR adds direction-aware cursor requests and backward iteration to shared synchronous and asynchronous pagination, with HTTP-level tests.

  • Follow-up requests retain the original query parameters while replacing the active cursor.
  • Backward iteration follows before and reverses each page; the findings concern ordering and an endpoint without backward metadata.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Initial page request] --> B{before without after?}
  B -- No --> C[Yield page data]
  C --> D[Fetch with after cursor]
  B -- Yes --> E[Yield reversed page data]
  E --> F{before metadata present?}
  F -- Yes --> G[Fetch with before cursor]
  F -- No --> H[Stop]
Loading

Reviews (1) · Last reviewed commit: "fix: page backward in auto_paging_iter a..."

Comment thread src/workos/_pagination.py
backward = page._direction == "backward"
while True:
yield from page.data
items = reversed(page.data) if backward else page.data

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Backward pages lose requested order When a caller uses order="normal" with a before cursor, results are documented as descending even though before fetches older records. This reversal yields each page in ascending order instead, so sync and async iteration no longer preserve the requested order.

Knowledge Base Used: Pagination and shared types

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/workos/_pagination.py
Line: 75

Comment:
**Backward pages lose requested order** When a caller uses `order="normal"` with a `before` cursor, results are documented as descending even though `before` fetches older records. This reversal yields each page in ascending order instead, so sync and async iteration no longer preserve the requested order.

**Knowledge Base Used:** [Pagination and shared types](https://app.greptile.com/workos/-/custom-context/knowledge-base/workos/workos-python/-/docs/pagination-and-shared-types.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment thread src/workos/_pagination.py
break
page = page._fetch_page(after=page.after)
if backward:
if page.before is None or page._fetch_page is None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Events pagination stops early list_events(before=...) returns pagination metadata with only an after cursor. This new backward branch requires page.before, so it reverses the first page and stops without fetching further results. The async iterator has the same behavior.

Knowledge Base Used: Pagination and shared types

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/workos/_pagination.py
Line: 80

Comment:
**Events pagination stops early** `list_events(before=...)` returns pagination metadata with only an `after` cursor. This new backward branch requires `page.before`, so it reverses the first page and stops without fetching further results. The async iterator has the same behavior.

**Knowledge Base Used:** [Pagination and shared types](https://app.greptile.com/workos/-/custom-context/knowledge-base/workos/workos-python/-/docs/pagination-and-shared-types.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

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

None yet

Development

Successfully merging this pull request may close these issues.

1 participant