Describe the bug
V2ListIterator (in src/autoPagination.ts) is missing the promiseCache
concurrency guard that V1Iterator uses to serialise parallel .next() calls.
If two pieces of code call .next() simultaneously while a page is in
flight — which can happen inside autoPagingEach under certain async
patterns — both calls independently reach turnPage(), issuing two HTTP
requests for the same next page. This produces duplicated or out-of-order
items in the result set.
V1Iterator solves this correctly:
next(): Promise<IteratorResult> {
if (this.promiseCache.currentPromise) {
return this.promiseCache.currentPromise;
}
...
}
V2ListIterator has no equivalent guard.
To Reproduce
Call .next() twice in parallel on a V2ListIterator while the first page
transition is in flight. Both calls independently invoke turnPage(),
resulting in two HTTP requests for the same page.
Expected behavior
Parallel .next() calls should be coalesced into a single in-flight promise,
exactly as V1Iterator does.
Code snippets
OS
Not environment-specific — reproduced by code inspection of src/autoPagination.ts. The missing promiseCache guard is absent regardless of operating system.
Node version
Reproducible on any supported Node version. Verified against Node 20 LTS. The bug is structural — V2ListIterator lacks the promiseCache pattern that V1Iterator implements — not a runtime behaviour difference between Node versions.
Library version
stripe-node master branch — confirmed in src/autoPagination.ts V2ListIterator class (current as of commit sha if known, otherwise master HEAD)
API version
Affects V2 API endpoints only (paths beginning with /v2/). The V2ListIterator is used exclusively for V2 list methods. API version: 2025-03-31.basil or later (any version using V2 list endpoints)
Additional context
V1Iterator (lines ~43–130) has this correct. V2ListIterator was added later
and did not carry the pattern over.
A related gap in the same class: after turnPage() returns, only one .next()
is attempted on the new iterator — so an empty intermediate page (data: []
with next_page_url set) silently terminates iteration. Both issues can be
fixed together if that's preferred.
Also happy to submit a PR once confirmed.
Describe the bug
V2ListIterator (in src/autoPagination.ts) is missing the promiseCache
concurrency guard that V1Iterator uses to serialise parallel .next() calls.
If two pieces of code call .next() simultaneously while a page is in
flight — which can happen inside autoPagingEach under certain async
patterns — both calls independently reach turnPage(), issuing two HTTP
requests for the same next page. This produces duplicated or out-of-order
items in the result set.
V1Iterator solves this correctly:
next(): Promise<IteratorResult> {
if (this.promiseCache.currentPromise) {
return this.promiseCache.currentPromise;
}
...
}
V2ListIterator has no equivalent guard.
To Reproduce
Call .next() twice in parallel on a V2ListIterator while the first page
transition is in flight. Both calls independently invoke turnPage(),
resulting in two HTTP requests for the same page.
Expected behavior
Parallel .next() calls should be coalesced into a single in-flight promise,
exactly as V1Iterator does.
Code snippets
OS
Not environment-specific — reproduced by code inspection of src/autoPagination.ts. The missing promiseCache guard is absent regardless of operating system.
Node version
Reproducible on any supported Node version. Verified against Node 20 LTS. The bug is structural — V2ListIterator lacks the promiseCache pattern that V1Iterator implements — not a runtime behaviour difference between Node versions.
Library version
stripe-node master branch — confirmed in src/autoPagination.ts V2ListIterator class (current as of commit sha if known, otherwise master HEAD)
API version
Affects V2 API endpoints only (paths beginning with /v2/). The V2ListIterator is used exclusively for V2 list methods. API version: 2025-03-31.basil or later (any version using V2 list endpoints)
Additional context
V1Iterator (lines ~43–130) has this correct. V2ListIterator was added later
and did not carry the pattern over.
A related gap in the same class: after turnPage() returns, only one .next()
is attempted on the new iterator — so an empty intermediate page (data: []
with next_page_url set) silently terminates iteration. Both issues can be
fixed together if that's preferred.
Also happy to submit a PR once confirmed.