Skip to content

fix(sdk): harden TemperClient pagination against truncation, loops, and bad envelopes - #441

Draft
rita-aga wants to merge 1 commit into
mainfrom
claude/sdk-pagination-hardening
Draft

fix(sdk): harden TemperClient pagination against truncation, loops, and bad envelopes#441
rita-aga wants to merge 1 commit into
mainfrom
claude/sdk-pagination-hardening

Conversation

@rita-aga

@rita-aga rita-aga commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Follows up the pagination fix (#438) with defects three fresh-context reviews found in it. TemperClient.list() could still silently truncate at its page ceiling, loop on a repeated nextLink, end early on a {}/{"value":null}/falsy-nextLink page, accept invalid page sizes, and break browser callers using a relative baseUrl.

What changed (packages/temper-sdk-ts/src/client.ts)

  • Throw, don't truncate: list() throws on a repeated nextLink (the real loop protection) and at a runaway page ceiling set far above any real traversal (so pageSize:1 over a huge set never false-throws), instead of returning a partial/duplicated result as if complete.
  • Validate the envelope: value must be an array; a @odata.nextLink, when present, must be a non-empty string — so a 200 with {}, {"value":null}, or a falsy non-string link can't silently end paging. Shared by list() and listPage().
  • Validate page size: reject 0, negative, fractional, NaN, Infinity.
  • Relative baseUrl: resolve nextLink against resp.url (always absolute), so a relative baseUrl (valid in the browser) still yields an absolute next URL.
  • Principal on reads: the configured principal is now sent on read requests, not only writes.

Decisions & Tradeoffs

  • Decision: the repeated-link guard is the loop protection; the page ceiling is only a runaway backstop, set very high. Chose over a low ceiling because a valid large traversal must not be rejected.
  • Decision: resolve against resp.url. Chose over requiring an absolute baseUrl — keeps existing Node callers working, unblocks browser callers, no API change.

Evidence

  • tsc --noEmit clean.
  • vitest run: 16/16 pass (9 new fetch-mocked tests: ceiling/repeated-link, missing/invalid/falsy envelope, invalid page sizes, relative-baseUrl resolution, listPage validation, principal forwarding).

Author: Claude Opus 4.8 (claude-opus-4-8), Claude Code harness.

🤖 Generated with Claude Code

Greptile Summary

The PR strengthens TypeScript SDK pagination by validating page sizes and OData envelopes, detecting repeated links, resolving relative links from the response URL, and forwarding configured principal headers. It also raises the runaway traversal ceiling from 10,000 to 10,000,000 pages, making the remaining unique-link failure mode capable of consuming excessive resources.

  • Adds strict page-size and response-envelope validation shared by list() and listPage().
  • Detects repeated pagination URLs and throws instead of returning partial results.
  • Resolves subsequent links against the fetched response URL.
  • Adds principal headers to read requests.
  • Expands fetch-mocked coverage for malformed pages, looping, relative URLs, and principal forwarding.

Confidence Score: 4/5

The pagination hardening should not merge until the unique-next-link runaway path is bounded before it can issue millions of requests and exhaust client resources.

Repeated links are stopped promptly, but a server generating distinct links can keep the client fetching and retaining state for up to ten million pages, materially weakening the previous runaway bound.

Files Needing Attention: packages/temper-sdk-ts/src/client.ts

Important Files Changed

Filename Overview
packages/temper-sdk-ts/src/client.ts Adds pagination and envelope hardening, but the ten-million-page ceiling is too high to provide an effective resource-exhaustion backstop for unique malformed links.
packages/temper-sdk-ts/test/client.test.ts Adds focused mocked-fetch coverage for the new validation, loop detection, URL resolution, and read-header behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Fetch current page] --> B[Validate OData envelope]
    B --> C[Append rows]
    C --> D{nextLink present?}
    D -- No --> E[Return complete result]
    D -- Yes --> F[Resolve against response URL]
    F --> G{URL already seen?}
    G -- Yes --> H[Throw repeated-link error]
    G -- No --> I{Page count below 10,000,000?}
    I -- No --> J[Throw ceiling error]
    I -- Yes --> A
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Codex Fix All in Cursor

Reviews (1): Last reviewed commit: "fix(sdk): harden TemperClient pagination..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

…nd bad envelopes

Follows up the pagination fix (#438) with the defects three fresh-context reviews
found in it.

- list(): throw at the 10k-page ceiling and on a repeated nextLink instead of
  returning a partial or duplicated result as if complete.
- validate the page envelope: value must be an array and a nextLink, when present,
  a non-empty string, so a 200 with {} or {"value":null} can't silently end paging.
- reject invalid page sizes (0, negative, fractional, NaN, Infinity).
- resolve nextLink against the response URL, not the request string, so a relative
  baseUrl (valid in the browser) still yields an absolute next URL.
- listPage(): validate the envelope and honour its return type.
- send the configured principal on read requests, not only writes.
- add 9 fetch-mocked tests covering these paths (16/16 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rita-aga

Copy link
Copy Markdown
Collaborator Author

@greptile review

Comment on lines +117 to +121
// this ceiling is only a runaway backstop, set far above any real traversal
// (even pageSize:1 over a huge set) so it never rejects valid pagination.
const MAX_PAGES = 10_000_000;
for (let page = 0; url; page++) {
if (page >= MAX_PAGES) throw new Error(`list ${entityType} exceeded ${MAX_PAGES} pages`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Runaway ceiling exhausts resources

When a misbehaving server continually returns distinct next links, each URL bypasses the repeated-link guard and remains retained while the client performs up to ten million sequential requests, causing excessive resource consumption long before the backstop fires.

Fix in Claude Code Fix in Codex Fix in Cursor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant