`az rest --url` returns malformed pagination URLs causing infinite loops
- Dominant language
- Python
- Stars
- 4.6k
- Forks
- 3.5k
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 60
Description
### Describe the bug
The Graph REST API has a limit of 100 entries per page. If one wants to fetch more records than that, there is a `@odata.nextLink` optional property in the API response to keep fetching in a loop. If I try to make a simple loop using `@odata.nextLink`, an infinite loop is created, because after the second page, the URL is malformed and it gets stuck on the second page.
### Related command
`az rest -u "https://graph.microsoft.com/v1.0/servicePrincipals"`
### Errors
Infinite loop due to pagination
### Issue script & Debug output
I am running the following command:
```bash
az rest -u "https://graph.microsoft.com/v1.0/servicePrincipals"
```
e.g.:
```bash
az rest -u "https://graph.microsoft.com/v1.0/servicePrincipals"
# output is fine, we take @odata.nextLink and call it again
az rest -u "https://graph.microsoft.com/v1.0/servicePrincipals?$skiptoken=RFNwdAIAAQAAADVTZ...redacted"
# we get output, but now the @odata.nextLink is malformed:
az rest -u "https://graph.microsoft.com/v1.0/servicePrincipals?=RFNwdAIAAQAAADVTZXJ2aWNl...-->this_shouldnt_be_here<--&$skiptoken=RFNwdAIAAQAAADVTZXJ2aW...actual_token"
# this will now only return the second page forever, and the @odata.nextLink will keep getting larger and larger
```
### Expected behavior
`@odata.nextLink` should not cause infinite loops
### Environment Summary
```bash
azure-cli 2.77.0
core 2.77.0
telemetry 1.1.0
Extensions:
rdbms-connect 1.0.6
Dependencies:
msal 1.34.0b1
azure-mgmt-resource 23.3.0
Python location '/opt/az/bin/python3'
Config directory '/home/octavian/.azure'
Extensions directory '/home/octavian/.azure/cliextensions'
Python (Linux) 3.13.7 (main, Aug 26 2025, 08:47:13) [GCC 12.2.0]
Legal docs and information: aka.ms/AzureCliLegal
Your CLI is up-to-date.
```
### Additional context
I was able to fix this by manually constructing the URL:
```ts
export async function fetchFromGraphApiWithPagination(
url: string,
options?: Omit & Omit,
): Promise {
const output: T[] = [];
let targetUrl = url;
while (true) {
const result = await executeCommand(`az rest -u "${targetUrl}"`, {
...options,
parseJson: true,
});
if (result?.value && Array.isArray(result.value)) {
output.push(...result.value);
}
// Manually handle skip token because Azure nextLinks are glitched and will infinite loop otherwise
if (result?.['@odata.nextLink']) {
const nextLink = new URL(result['@odata.nextLink']);
const skipToken = nextLink.searchParams.get('$skiptoken');
if (!skipToken) {
clack.log.error(
`ERROR: Azure Graph REST API pagination is glitching out.\nNext URL is missing skip token.\nRecord fetching cancelled to prevent infinite loop.\nYou will likely be missing some records.`,
);
break;
} else {
const newUrl = new URL(url);
newUrl.searchParams.set('$skiptoken', skipToken);
targetUrl = newUrl.toString();
}
} else {
break;
}
}
return output;
}
```
Contributor guide
Assessment
This issue has not been assessed yet.