Skip to content

az rest --url returns malformed pagination URLs causing infinite loops #32080

@Roman-Octavian

Description

@Roman-Octavian

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:

az rest -u "https://graph.microsoft.com/v1.0/servicePrincipals"

e.g.:

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

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:

export async function fetchFromGraphApiWithPagination<T>(
  url: string,
  options?: Omit<shell.ExecOptions, 'async'> & Omit<ExtraOptions, 'exitOnFailure'>,
): Promise<T[]> {
  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;
}

Metadata

Metadata

Assignees

Labels

Auto-AssignAuto assign by botService AttentionThis issue is responsible by Azure service team.bugThis issue requires a change to an existing behavior in the product in order to be resolved.customer-reportedIssues that are reported by GitHub users external to the Azure organization.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions