Share via

Graph API Calendar Events delta query infinite loop

Alberto Romei 115 Reputation points
2026-02-27T10:12:29.5566667+00:00

I'm using Event delta queries API to synchronize calendar events for several account. The initial /calendar/calendarView/delta request spans a 1-year window, from now to 1 year in the future. With one specific account, the delta request return an infinite number of pages, all with the same contents. The response to /delta request contains a @odata.nextLink property pointing to the next page of results. The second page of results is actually different from the first page, and contains another nextLink for the third page. After that, all subsequent requests will return the same page contents. Note that the nextLink is always different on each page (seems legit), but the only difference from one response to the next is the nextLink itself. The loop will go on forever, always downloading the same content.

I wrote a small bash script to demonstrate the issue, writing the JSON responses to disk (one file per page):

#!/bin/sh

export ACCESS_TOK='ey...'

# Page number
N=0

# Output file (one per page)
outf="${N}.json"

curl --get -v -H 'Authorization: Bearer '${ACCESS_TOK} \
  'https://graph.microsoft.com/v1.0/users/*****@*****.com/calendar/calendarView/delta' \
  --data-urlencode 'startDateTime=2026-02-26T15:00:00+0100' \
  --data-urlencode 'endDateTime=2027-02-26T15:00:00+0100' >${outf}

N=$((N+1))  

while true ; do
  nextL=`jq -r '."@odata.nextLink"' ${outf}`
  
  [[ "$nextL" == "null" ]] && break
  
  outf=${N}.json
  
  echo "req: $nextL"
  
  # Follow nextLink
  curl -H 'Authorization: Bearer '${ACCESS_TOK} \
  	"${nextL}" >${outf}
  
  N=$((N+1))  
done

... and stopped it after 4587 pages and 861MB of downloaded content.

-rw-r--r-- 1 calsync calsync 199525 Feb 25 15:59 0.json
-rw-r--r-- 1 calsync calsync 195225 Feb 25 15:59 1.json
-rw-r--r-- 1 calsync calsync 195225 Feb 25 15:59 2.json
-rw-r--r-- 1 calsync calsync 195225 Feb 25 15:59 3.json
-rw-r--r-- 1 calsync calsync 195225 Feb 25 15:59 4.json
... ... ...
... ... ...
-rw-r--r-- 1 calsync calsync 195225 Feb 25 16:30 4585.json
-rw-r--r-- 1 calsync calsync 195225 Feb 25 16:30 4586.json
-rw-r--r-- 1 calsync calsync 195225 Feb 25 16:30 4587.json

As you can see here, i used jq to format the JSON files in order to diff them more easily. In the ~190k of payload, the only thing that changes from one page to another (except page zero) is the nextLink:

$ diff <(jq . 1.json) <(jq . 2.json)
4837c4837
<   "@odata.nextLink": "https://graph.microsoft.com/v1.0/users/*****@*****.com/calendar/calendarView/delta?$skiptoken=3jcBbtAJKmoRXgEi7wAb_b-SnM7RKiISGlxJAy7BDn7aM2vqoQuAPxccRYP4_Tux66HiXGWtsIfug5zkH1mMoFlNIuQpw7GCRaL-zLosn2SkVr7vHT_fUUPMANRBa7tYB8XTL1R_qrZFkjGNLA7GWQvEX2RVfyMSvE7IAAele0dZuYBDOyzfDuesGU0SB-9j4Hsg3I5vRuKv5_FGhHU1mOLg_8WtM5Hc5_OGliG2oYMI6PvpQOiwN8XBqPEhH6A-6fEazgh_PbId8X6py93j1g.y0hlsZbeZARizXvTqxcMjWcV9yV65K8MzLwAMee33Mo"
---
>   "@odata.nextLink": "https://graph.microsoft.com/v1.0/users/*****@*****.com/calendar/calendarView/delta?$skiptoken=3jcBbtAJKmoRXgEi7wAb_b-SnM7RKiISGlxJAy7BDn7aM2vqoQuAPxccRYP4_Tux66HiXGWtsIfug5zkH1mMoFlNIuQpw7GCRaL-zLosn2SkVr7vHT_fUUPMANRBa7tYmc7PT1BmZ6_ay1eBkiRy66vv0-ul3kufSSlfyfr8DiBFef8O0N8BEajVKudMjW61LaNfLntk521MRvdrdMgNoGRGPHhCfwI7cFV9yzyHYiuLTvmdeGODzi7mME9YT1SMaRK80Yel1BiDrPPN6gDuZw.Eo3gVAnWzr_rRvU3J2a_OCI2olGSJHlG4jWW1lYRUig"


$ diff <(jq . 1.json) <(jq . 4587.json)
4837c4837
<   "@odata.nextLink": "https://graph.microsoft.com/v1.0/users/*****@*****.com/calendar/calendarView/delta?$skiptoken=3jcBbtAJKmoRXgEi7wAb_b-SnM7RKiISGlxJAy7BDn7aM2vqoQuAPxccRYP4_Tux66HiXGWtsIfug5zkH1mMoFlNIuQpw7GCRaL-zLosn2SkVr7vHT_fUUPMANRBa7tYB8XTL1R_qrZFkjGNLA7GWQvEX2RVfyMSvE7IAAele0dZuYBDOyzfDuesGU0SB-9j4Hsg3I5vRuKv5_FGhHU1mOLg_8WtM5Hc5_OGliG2oYMI6PvpQOiwN8XBqPEhH6A-6fEazgh_PbId8X6py93j1g.y0hlsZbeZARizXvTqxcMjWcV9yV65K8MzLwAMee33Mo"
---
>   "@odata.nextLink": "https://graph.microsoft.com/v1.0/users/*****@*****.com/calendar/calendarView/delta?$skiptoken=3jcBbtAJKmoRXgEi7wAb_b-SnM7RKiISGlxJAy7BDn7aM2vqoQuAPxccRYP4_Tux66HiXGWtsIfug5zkH1mMoFlNIuQpw7GCRaL-zLosn2TvJpfKv-_hx9AzlfoD7vjgDwerYtwCYesYPrYCpysQOe51HoPAPmrrVT6hsMpjSzvBDO91Aq2uUBgJgMrd0Pkrb_9Ffo2tdtCSS5pqGzWApTEBinusvQnwuIrZSyAugqhbmqQHANzQFUepUvIzgXEpvYpevOhjckVNQGGxpahXOw.wNwfHHevjZFh1KEuAdNYuuegkwwiNm8Cw2O9Y_1_PG4"

I found many similar reports on the web, regarding other areas of the Graph API, but no clear solution.

https://github.com/OneDrive/onedrive-api-docs/issues/1424

https://github.com/microsoftgraph/msgraph-sdk-javascript/issues/1656

https://dori-uw-1.kuma-moon.com/en-us/answers/questions/5519318/graph-api-groups-delta-query-never-returns-delta-q

Not being able to find useful info, i've made several random tests, and finally discovered a behavior that seems to solve the problem. My initial /delta request did not specify the optional Prefer: odata.maxpagesize header, leaving the default page size. If i set it to 2 or 100, the infinite loop does not occur. If i set it to 2000, or leave it unset, i get the endless loop again.

Did someone experience a similar problem ? Can someone confirm that explicitly setting the maxsize header is a reliable solution to the problem ?

Thanks, Alberto

Microsoft Security | Microsoft Graph

Answer accepted by question author
  1. Justin Moles 90 Reputation points
    2026-03-06T15:45:57.5+00:00

    So we have an application that's been making delta calls since 2018 with no issues with page size. We started seeing issue on February 23rd. We've tried a variety of solutions but a page size that would work for one calendar would fail for another and sometimes when retrying would work later on. It wasn't predictable. What we ended up implementing was a dynamic switching of page size. We created a checker for infinite loop (by checking if ALL events sent to us in current page were previously sent) and if loop was detected, we restarted delta with new page size. For our particular use case did page sizes of 1000, 500, 250, 100, 50, 25. If last page size is reached and infinite loop is still detected, we give up. So far this has effectively eliminated the issue however it's extremely annoying to have implement a fairly complex solution like this to what should be a simple issue for Microsoft to fix.

    3 people found this answer helpful.

4 additional answers

Sort by: Most helpful
  1. Long Nguyen Xuan 6 Reputation points
    2026-03-05T02:17:17.4633333+00:00

    I am experiencing the same issue on our system. The workaround is to compare the event IDs from the current delta link and the next link. If the data is the same, we break the sync to avoid an infinite loop.

    1 person found this answer helpful.

  2. Deleted

    This answer has been deleted due to a violation of our Code of Conduct. The answer was manually reported or identified through automated detection before action was taken. Please refer to our Code of Conduct for more information.


    Comments have been turned off. Learn more

  3. Pierre Charlet 0 Reputation points
    2026-04-02T15:24:25.4566667+00:00

    When will we have a solution implemented on Microsoft’s side? This issue impacts the core of our application. Implementing the proposed workaround is complex, would require extensive testing before deployment, and would only partially resolve the problem.

    0 comments No comments

  4. Deleted

    This answer has been deleted due to a violation of our Code of Conduct. The answer was manually reported or identified through automated detection before action was taken. Please refer to our Code of Conduct for more information.


    Comments have been turned off. Learn more

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.