This topic was automatically generated from Slack. You can find the original thread here.
Bug: gong-get-extensive-data returns the same page 6× because the cursor is sent as a query string instead of in the POST body
This topic was automatically generated from Slack. You can find the original thread here.
Bug: gong-get-extensive-data returns the same page 6× because the cursor is sent as a query string instead of in the POST body
Repo: PipedreamHQ/pipedream
Component: components/gong/
Action: gong-get-extensive-data (key gong-get-extensive-data, version 0.0.5)
The shared paginator in gong.app.mjs injects the Gong cursor into params (query string). That’s correct for GET /v2/calls (used by gong-list-calls) but wrong for POST /v2/calls/extensive (used by gong-get-extensive-data), where Gong’s API expects the cursor in the JSON request body. Gong therefore ignores the cursor on every iteration, returns page 1 each time, the action concatenates the duplicates, and terminates at the DEFAULT_MAX = 600 safety cap. The caller receives ~100 unique calls duplicated 6× with no signal of truncation.
components/gong/gong.app.mjs — getResourcesStream, lines 147-197.components/gong/actions/get-extensive-data/get-extensive-data.mjs — calls app.paginate(...) inside run, line 200ish.components/gong/common/constants.mjs — defines DEFAULT_MAX = 600.In gong.app.mjs:147-197, getResourcesStream loops like this:
js
async **getResourcesStream({ resourceFn, resourceFnArgs, resourceName, max }) {
let cursor;
let resourcesCount = 0;
while (true) {
response = await resourceFn({
...resourceFnArgs,
params: { ...resourceFnArgs?.params, cursor }, // :warning: cursor goes in query string
});
const nextResources = resourceName && response[resourceName] || response;
if (!nextResources?.length) return;
for (const resource of nextResources) {
yield resource;
resourcesCount += 1;
if (resourcesCount >= max) return; // :warning: caps at DEFAULT_MAX (600)
}
if (!response.records?.cursor) return;
cursor = response.records.cursor;
}
}
The action that gets paginated is defined in get-extensive-data.mjs:
js
methods: {
getExtensiveData(args = {}) {
return this.app.post({ path: "/calls/extensive", ...args });
},
},
async run({ $ }) {
// ...
const calls = await app.paginate({
resourceFn: getExtensiveData,
resourceFnArgs: {
step: $,
data: { filter, contentSelector }, // :warning: filter+selector go in body
},
resourceName: "calls",
max: maxResults,
});
}
The wrapper calls this.app.post({ path, ...args }), which delegates to makeRequest({ method: "post", ...args }), which spreads args into the axios config. Axios then sends:
{ filter, contentSelector } (from the data field)POST /v2/calls/extensive?cursor=value (from the params field)Per Gong’s documented API for POST /v2/calls/extensive (the very link in the action’s own description field, Gong | Gong public API docs), the cursor is a property of the JSON request body, not a query parameter. Gong ignores the query-string cursor and treats every request as page 1. The response carries a fresh cursor each time (because Gong is just issuing the next-page cursor for page 1), so the loop’s if (!response.records?.cursor) return; exit is never taken. The loop ultimately terminates only when resourcesCount >= max fires at 600.
components/gong/actions/list-calls/list-calls.mjs wraps GET /v2/calls. It exposes a top-level cursor prop and forwards it explicitly:
js
return app.listCalls({
step,
params: { ...params, cursor },
// ...
});
For a GET, params is the query string and Gong does respect it there. So gong-list-calls paginates fine — verified empirically in the same Gong workspace with the same OAuth connection: page 0 returns 100 calls plus cursor eyJ...UEUQ; page 1 (called with that cursor) returns a different 100 calls plus a different cursor.
The bug is specific to combining the generic getResourcesStream paginator with a POST endpoint.
A single invocation in production with these inputs:
json
{
"fromDateTime": "2026-04-15T00:00:00Z",
"toDateTime": "2026-04-30T23:59:59Z",
"primaryUserIds": ["3233735285032703158", "6253634635529901593", "9158638369112583040"],
"includeParties": true
}
Returned:
$summary: "Successfully retrieved data for 600 calls"metaData.id0–99, 100–199, 200–299, 300–399, 400–499, 500–599 are *byte-identical to each otherrecords[0] == records[100] as full JSON equality, including the nested parties arrayThe latest call’s scheduled field across all 100 unique calls is 2026-04-28T16:00:00-04:00 — within a query window that ran through 2026-04-30T23:59:59Z. The Gong workspace had additional calls on April 29 and April 30 that never made it into the response. There is nothing in the returned payload signaling the truncation: records, cursor, currentPageNumber, totalRecords, and requestId are all stripped because paginate(...) flattens to the bare item array.
The DEFAULT_MAX = 600 cap matches the observed termination exactly: 6 iterations × 100 calls per page.
gong-get-extensive-data with that window via fromDateTime/toDateTime (optionally narrowed by primaryUserIds).metaData.id. If unique count is exactly 100 and total count is exactly min(maxResults, 600), and unique count ≤ raw count divides evenly into the raw count, the bug is reproducing.