SharePointVault ~/gotchas/permissions ⌕ Search 404 pages, error messages, scripts…Ctrl K ★ Star
M↓ README.mdM↓ site-audience-from-associated-groups-misses-nested-groups.md
sharepoint-vault›gotchas›permissions›site-audience-from-associated-groups-misses-nested-groups.mdhistoryedit on GitHub
● GOTCHApermissions· 3 min read

Counting a site's audience from its associated groups silently misses nested groups

applies to: SharePoint Onlinereviewed: 2026-09-28#permissions#rest-api#site-groups#read-receipts

A "who has not read this yet" list or a "12 of 48 have read it" figure needs the size of the site's audience. Reading the users of the Owners and Members groups looks like the answer, but on a typical communication site the employees are in Visitors through Everyone except external users, and on a group-connected team site the members sit behind the Microsoft 365 group claim. Both are a single row with PrincipalType 4. Filter to PrincipalType 1 and the audience of a company-wide portal becomes the one site owner. If any associated group contains a group, the audience is unknown – say so instead of showing a list built from the direct members.

Seznam „kdo ještě nečetl“ nebo údaj „přečetlo 12 z 48“ potřebuje velikost publika webu. Čtení uživatelů skupin Vlastníci a Členové vypadá jako odpověď. Na běžném komunikačním webu jsou ale zaměstnanci v Návštěvnících přes Everyone except external users a na týmovém webu se skupinou Microsoft 365 stojí členové za claimem skupiny. Obojí je jediný řádek s PrincipalType 4. Kdo nechá jen PrincipalType 1, zmenší publikum celofiremního portálu na jednoho vlastníka webu. Obsahuje-li kterákoli přidružená skupina skupinu, je publikum neznámé – řekni to, místo seznamu z přímých členů.

Symptom

A read-receipt feature on an intranet home page shows "Not read (1)" and "1 of 1 have read it" on a portal that the whole company reads. Nothing errors; the numbers are simply small and look plausible to whoever tests with a handful of accounts.

Cause

The associated groups hold principals, not only people:

GET /sites/portal/_api/web/AssociatedOwnerGroup/Users?$select=Title,Email,PrincipalType
→ [ { "PrincipalType": 1, ... } ]                       // one owner

GET /sites/portal/_api/web/AssociatedMemberGroup/Users?$select=Title,Email,PrincipalType
→ [ ]

GET /sites/portal/_api/web/AssociatedVisitorGroup/Users?$select=Title,Email,PrincipalType,LoginName
→ [ { "PrincipalType": 4,
      "LoginName": "c:0-.f|rolemanager|spo-grid-all-users/<tenant-guid>" } ]   // Everyone except external users

PrincipalType: 1 = user, 2 = distribution list, 4 = security group (including the tenant-wide claims and the Microsoft 365 group claim c:0o.c|federateddirectoryclaimprovider|<group-guid>), 8 = SharePoint group. The code that kept only PrincipalType 1 rows and read only Owners and Members produced an audience of one.

Fix

  • Read all three associated groups – Owners, Members and Visitors. Visitors is where communication sites put their readers.
  • Any row with PrincipalType other than 1 makes the audience unknown. Return an explicit "unknown" with a reason (a nested group, or no group readable), not a partial list. The UI then hides "not read" and percentages and says why. The list of people who did confirm stays accurate, because it comes from the receipts themselves.
  • Don't try to expand Everyone except external users into a list of people. It stands for every internal account in the directory; SharePoint has no call that enumerates it. A Microsoft 365 group can be expanded with Graph (/groups/{id}/transitiveMembers), but that needs a Graph permission with admin consent (least privileged: GroupMember.ReadBasic.All). That's a product decision, not a quiet fallback.
  • Test on a site set up like production. A test site where you added three people directly to Members hides the whole problem.

Notes

  • The same trap applies to anything that divides by "site members": completion rates, "X of Y acknowledged", "everyone has seen it" badges.
  • $top=5000 on the group users call is still worth keeping: a group with thousands of directly added users exists too.
Was this helpful?ask or add your experience on GitHub →
Saved you an hour? A star on GitHub helps other SharePoint people find the vault.★ Star on GitHub
last reviewed: 2026-09-28last updated: 2026-09-28historyedit on GitHubview source