## TL;DR
Find the provider's real end-of-list signal before writing the loop condition. If the provider signals the last page with an empty data array, the loop must break on empty data - not on a `has_more` field that does not exist in the response. One logged sample of the last two pages reveals the true signal.

## The query

```text
agent parsed has_more from the response body  -  the API signals the last page with an empty data array instead
```

## Steps

### 1. Capture the last two pages of a real list

Paginate a list manually (or with logging on the generated client) and save the full response bodies of the final pages. Read them, do not skim the docs.

Expected: the actual last-page shape - an empty data array, with or without any has_more field.

### 2. Name the real termination signal

From the captured responses, write down exactly what marks the end: empty data array, a null or missing next cursor, a has_more false, or a total-count reached. There is exactly one source of truth and it is the wire, not the docs.

Expected: one sentence naming the real signal, e.g. "the list ends when the data array is empty."

### 3. Rewrite the loop condition around the real signal

Replace the `has_more` check with the observed signal. If the signal is an empty data array, break when the array is empty after processing any items. Keep the condition to exactly the observed signal - do not stack guesses.

Expected: a loop condition that matches the provider's observed behavior.

### 4. Add a backstop for the unexpected

Keep a maximum page count so that if the provider changes its signal later, the loop fails loudly instead of running forever. Log the page count and last response shape when the backstop trips.

Expected: the loop cannot run forever under any provider behavior change.

### 5. Test against a list with a known size

Paginate a list whose item count you know, including the edge case of a list whose size is an exact multiple of the page size. Confirm the client stops after the correct number of items.

Expected: exact item count fetched, loop exited, no extra empty-page requests beyond the one that signals the end.

## Use this when

- The generated code reads `has_more` (or similar) from a response that never contains it
- The loop makes extra requests past the end of the list, or never terminates
- The provider's last page is an empty data array
- A regeneration reintroduced the `has_more` assumption

## Not for this skill when

- The provider does send `has_more` and the code misreads it (parsing bug)
- The loop terminates but pages contain duplicates (dedupe problem)
- Requests fail with 429s mid-pagination (rate-limit problem)
- The list is genuinely unbounded (streaming endpoint, different design)

## Variant phrasings

### loop made one extra request after the last page

If the provider signals the end with an empty page, one trailing empty request is the correct behavior, not a bug. Step 3 implements exactly that.

### generated client never stopped paginating

The `has_more` lookup on a missing field never becomes false in some languages (undefined is falsy in some, truthy-checks differ). Step 3 replaces the phantom field with the real signal.

## Why it happens

`has_more` is the best-known pagination signal, so the agent generated it by default without checking the provider's actual contract. The provider chose a different convention - the empty data array - and the docs buried it in a paragraph the agent skimmed. The loop condition references a field that is always absent, so it never fires, and the client keeps asking for pages that will never come.

## Edge cases

- Provider sends both an empty array AND a has_more false: either signal works; prefer the documented one and keep the other as a secondary check.
- Empty data array mid-list with more pages after (sparse pages): rare, but if the provider does this, the signal is the cursor, not the array. Step 1's capture of the last two pages distinguishes the two cases.
- List size is an exact multiple of page size: the client must make one final request to learn the list ended. That trailing request is correct; do not "optimize" it away.
- Provider changes the signal in a new API version: the backstop in step 4 turns a silent behavior change into a loud, diagnosable failure.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_McCFBTZpexjKlq5Afu-oig
