-
Notifications
You must be signed in to change notification settings - Fork 316
Description
Checklist
- I have looked into the Readme, Examples, and FAQ and have not found a suitable solution or answer.
- I have looked into the API documentation and have not found a suitable solution or answer.
- I have searched the issues and have not found a suitable solution or answer.
- I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- I agree to the terms within the Auth0 Code of Conduct.
Description
Package
- Package:
auth0(Node.js SDK) - Version: v5.x (Management API client)
Summary
The hasNextPage() function in paginated list methods (e.g., roles.list(), clients.list(), clientGrants.list()) uses a different default value for per_page than the API request, causing infinite loops when per_page is not explicitly provided.
Description
When calling a paginated list method without specifying per_page, the SDK:
- Sends the API request with
per_page=50(from destructured defaults) - Evaluates
hasNextPage()usingrequest?.per_page ?? 1(falls back to 1)
This mismatch causes hasNextPage() to incorrectly return true when there are 1-49 items, leading to infinite pagination loops.
Affected Methods
Methods using the page/per_page pagination pattern with this buggy hasNextPage logic:
hasNextPage: (response) => (response?.items ?? []).length >= (request?.per_page ?? 1)AFFECTED (page/per_page pattern):
roles.list()clients.list()organizations.members.roles.list()organizations.enabledConnections.list()organizations.invitations.list()- And others using the same pattern...
NOT AFFECTED (cursor-based pattern using from/take with response.next):
clientGrants.list()- useshasNextPage: (response) => response?.next != nullconnections.list()- useshasNextPage: (response) => response?.next != nullorganizations.list()- useshasNextPage: (response) => response?.next != nullorganizations.members.list()- useshasNextPage: (response) => response?.next != null
The cursor-based methods check response.next for pagination, which is a safe pattern that doesn't depend on comparing counts against page size.
Suggested Fix
Option 1: Use the same default in hasNextPage:
hasNextPage: (response) => (response?.roles ?? []).length >= (request?.per_page ?? 50)Option 2: Capture the effective per_page and use it in the closure:
const effectivePerPage = request?.per_page ?? 50;
// ...
hasNextPage: (response) => (response?.roles ?? []).length >= effectivePerPageWorkaround
Always explicitly pass per_page when calling list methods:
// Works correctly
const page = await client.roles.list({ per_page: 50 });Environment
- Node.js: v20.x
- auth0 SDK: v5.x
- OS: macOS / Linux
Reproduction
Steps to Reproduce
const { ManagementClient } = require('auth0');
const client = new ManagementClient({
domain: 'your-domain.auth0.com',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
});
// Assume you have 8 roles in your tenant
async function listAllRoles() {
const allRoles = [];
let page = await client.roles.list(); // No per_page specified
allRoles.push(...page.data);
// BUG: hasNextPage() checks 8 >= 1 → true (should check 8 >= 50 → false)
while (page.hasNextPage()) {
console.log('Fetching next page...'); // This prints forever!
page = await page.getNextPage();
allRoles.push(...page.data);
}
return allRoles;
}Expected Behavior
hasNextPage() should return false when the number of items returned is less than the effective per_page used in the API request (50 by default).
Actual Behavior
hasNextPage() returns true when items >= 1, causing infinite loops when there are 1-49 items.
Root Cause
In the generated SDK code (e.g., roles/client/Client.js):
// Line 91 - API request uses default of 50
const { per_page: perPage = 50, page = 0, ... } = request;
// Line 161 - hasNextPage uses default of 1
hasNextPage: (response) => {
return (response?.roles ?? []).length >= (request?.per_page ?? 1);
// ^^^^^^^^^^^^^^^^^^^^
// Should use perPage (50), not request?.per_page ?? 1
}Additional context
Additional Frustration: Inconsistent API Response Formats
During debugging, we discovered that the Auth0 Management API returns different response formats depending on whether pagination query parameters are provided. This behavior is not well-documented and made debugging significantly more difficult.
Without pagination params (GET /api/v2/roles):
[
{ "id": "rol_xxx", "name": "Admin", "description": "..." },
{ "id": "rol_yyy", "name": "Editor", "description": "..." }
]Returns a plain array of role objects.
With pagination params (GET /api/v2/roles?page=0&per_page=50):
{
"roles": [...],
"length": 8,
"total": 8
}Returns an object with roles array and pagination metadata.
This inconsistency, combined with the SDK's internal response wrapping (the Page<T> object with .data, .hasNextPage(), .getNextPage()), made it extremely difficult to:
- Understand what the raw API was returning
- Debug why pagination was behaving unexpectedly
- Set up proper mock responses for testing
We spent considerable time investigating whether our wiremock responses were formatted correctly, when the actual issue was the SDK's hasNextPage() logic. Clearer documentation on the API response format variations would help developers avoid this confusion.
node-auth0 version
5
Node.js version
v20.18.3