Skip to content
Ops Log
Lab NoteAI & Automation20 August 20266 min read

A Graph meeting export can return an empty page that still carries a next link

During a planned Microsoft Graph service update, paginated calls to getAllRecordings or getAllTranscripts can return HTTP 200 with an empty collection alongside a next link, then restart and re-serve items. An export loop that exits on no items stops there and reports success over a short archive.

Cover image for A Graph meeting export can return an empty page that still carries a next link
MJ
Michal Jatczak
Founder, ITSailor

A paginated call to getAllRecordings or getAllTranscripts can return HTTP 200 OK with an empty collection while still carrying an @odata.nextLink, after which pagination restarts and serves items it already served. Microsoft expects the service update behind this to complete by 31 August 2026. An expectation is not a commitment, and this note takes the narrower reading.

Last verified: 2026-08-20.

What the vendor says

From the Microsoft Graph known issues page, Teamwork and communications, read 2026-08-20:

A request can return a 200 OK response with an empty collection and an @odata.nextLink. Pagination then restarts and can return recording or transcript items that were returned previously.

The same entry calls it "a planned service update that is expected to be completed by August 31, 2026", and says such requests "may experience an automatic pagination token reset". The workaround names both halves: "Continue following @odata.nextLink even when the collection is empty. De-duplicate subsequent items by tracking the id property of each recording or transcript."

Why the empty page is easy to misread

The entry is headed around duplicate items, and duplicates are the half an administrator remembers: the quick reading is that an export over-collects for a few weeks and is cleaned up later. The misreading is not that duplicates are harmless, it is that they look like the only failure mode on offer while the same paragraph describes silent truncation. A loop that exits on an empty page rather than on a missing continuation token terminates at the reset, having seen no error, and records a clean run. Microsoft states the correct exit condition on its own Teams export guidance: "If no skipToken is present, it means that there are no more results to retrieve in the current batch".

A run that stops at the empty page finishes early with 200 on every call, writes no error line, and produces an archive short by an unknown amount. Nothing separates "the tenant has no more artifacts" from "the token reset and the loop stopped", and the shortfall surfaces only when a named meeting is asked for. The mirror error inflates instead: following the next link past the empty page without de-duplicating on the id property reports a count that is too high, and once it is in a retention report the fix is a retraction.

Test conditions

Run on 2026-08-20: five Microsoft Learn pages fetched live and read in full. Two prerequisites the export guidance states and this check depends on: an active Microsoft Teams licence must be assigned to the users whose data is exported, and these are protected APIs, so the app registration must meet Microsoft's requirements for accessing without a user. No tenant run has been made; it is the outstanding step. Both functions are application permission only, so this cannot be run by a signed-in administrator. The getAllTranscripts reference prints "Not supported." for delegated accounts.

http
# Read-only. Every request is a GET. Nothing is created or deleted.
# Do NOT add $top: a separate known issue can then suppress the next link.

GET /v1.0/users/{organizerId}/onlineMeetings/getAllTranscripts(meetingOrganizerUserId='{organizerId}')
Authorization: Bearer {app-only token, OnlineMeetingTranscript.Read.All}

# Then replay the @odata.nextLink value EXACTLY as returned. Append nothing,
# reapply no filter. The token already encodes the parameters.
GET {value of @odata.nextLink from the previous response}

# Stop ONLY when there is no @odata.nextLink. Do not stop on an empty value array:
# that page is what this check exists to observe. Same shape for getAllRecordings.

Results

Record two numbers per run. TOTAL is the count of objects across every value array returned; DISTINCT is the count of unique id values among them. DISTINCT below TOTAL means the reset has already occurred here. Also record whether any page returned 200 with an empty value array while still carrying a next link: that page is the whole finding, because a loop keyed on emptiness would have stopped there and reported success.

Failures

Three checks against the vendor documentation itself failed on 2026-08-20. The guidance on $top contradicts itself: the known issues page says "Do not pass $top query parameter until the issue is fixed", while both endpoint references list it as a supported optional parameter. Plan against the known issues page: Microsoft updated it for this defect, both references link to it by note, and the delta reference repeats the warning.

PageWhat it says about $topCarries the $top next-link warning
Known issues in Microsoft GraphDo not pass it until fixedYes
onlineMeeting: getAllTranscriptsSupported optional parameterNo
onlineMeeting: getAllRecordingsSupported optional parameterNo
callTranscript: deltaNot flagged in its tableYes, own Known issues section

Second, the known issues page crosses its own delta links: in the change-tracking sentence, getAllTranscripts resolves to the callRecording delta page and getAllRecordings to the callTranscript delta page. Third, six of the eight language tabs on both references issue a Get onlineMeeting by id rather than calling these functions at all, so the check above is raw HTTP and names no cmdlet.

Limitations

The check detects duplicates, not truncation. A loop that already exits on the empty page never fetches the duplicates, so the totals look perfect while the completeness of the archive is unverifiable: Microsoft documents a restart, not a loss, so the shortfall may be every artifact past the reset point or none at all. Truncation needs an independent count of what the tenant should hold, and ordering gives no help: the same guidance states, of the recordings and transcripts responses themselves, that "Results aren't guaranteed to be sorted by createdDateTime."

A clean run proves nothing about the next. The vendor writes "may experience" and names no trigger, frequency, cloud, tenant class or licence tier, so absence in a lab tenant does not clear a customer tenant.

A short result has innocent explanations too. The known issues page states that "The getAllRecordings API does not return recordings for meetings that don't have transcription enabled", so the count is not the tenant's recording population, and neither function returns channel meeting artifacts. An administrator can also switch off Graph access to transcripts, returning 403 with GraphAccessToTranscriptsDisabled: branch on the inner error code first.

Decision: act now

Act now, for two independent reasons. The defect is live today and the vendor date is eleven days out, framed as an expectation rather than a commitment, so keep the check past 1 September until a run shows the behaviour is gone. The stronger reason: the absence of the continuation token is the correct exit condition at all times, so a loop rewritten to test the link rather than the item count is right whichever way the date falls. Read the export code and record which field its loop tests.

An archive whose completeness was never measured is an assertion. Workflow engineering is the path when a Teams export pipeline needs an independent review of what it retrieved.

Sources and further reading

Was this field note useful?
Use the evidence

Test the same boundary in your environment.

Use a focused diagnostic to compare the lab result with the controls and constraints in your own environment.

Choose a diagnostic