Top 10 throttling and limits traps
SharePoint and its neighbours enforce limits that stay invisible until a list grows or a loop speeds up. These ten cost the most time, from the 5,000 item threshold to 429 responses that quietly corrupt data.
- The 5,000-item list view threshold — and why indexes must come earlyThe limit counts scanned rows, so past 5,000 items a filter on an unindexed column fails however few rows would match.It's scanned rows, not returned rows; index early, page always
- Unbounded Promise.all fan-out over a customer-sized collection invites HTTP 429A Promise.all sized by customer data works in test and gets throttled in production; bounded batch and worker pool patterns.Fan-out is safe when YOUR code decides the count, dangerous when the customer's data does; bounded batch and worker-pool patterns
- Retrying a throttled call is safe for GET onlyBlind retries on 429 duplicate writes because there is no idempotency key; retry reads freely and treat writes with care.429 means "not now", not "nothing happened"; no idempotency key exists, so a retried write duplicates the row (+ RateLimit-* headers are not guaranteed for delegated calls)
- Title and Created are the fields you filter on — and the ones your provisioning code never indexesProvisioning indexes only its own columns, so Title and Created stay unindexed and reads start failing past 5,000 items.Provisioning indexes only the columns it creates, so the most-filtered fields stay unindexed; past 5,000 items the read throttles and a swallowed error in a write path stops all writes silently
- Read all items from a large list — paging done right$top caps at 5,000 and $skip is ignored; follow nextLink and never return a partial list that looks complete.Any list past a few thousand items: $top caps at 5,000, $skip is ignored — follow odata.nextLink. Plus the error branch: break returns a partial list that looks complete
- A column called MyField0 appears — a throttled existence check or two web parts provisioning at onceA throttled existence check reads as missing and the column is created again as MyField0; use a three state check.POST /fields with a taken name succeeds with a numeric suffix; 429 read as "missing", a cached check or two concurrent web parts each make a twin — three-state check, single run per site, name check after the create
- A throttled permission probe must not downgrade — and cache — the roleCache a 429 permission probe and the user is stuck read only for the whole TTL; persist only a confirmed 200 result.A 429/403 on currentuser/groups resolves to the lowest role; cache it and the user is stuck read-only for the TTL; only persist a confirmed (200) result
- A page-size cap reported as a findingA round number like 100 or 5000 in a report is often the request limit, not the tenant; learn to spot it before it is cached.A round number (100 / 500 / 999 / 5000) is the tell; the limit of the request presented as a measurement of the tenant, then cached
- A REST write over 2 MB fails with "The request message is too big"Any REST body over 2 MB is rejected even though the field can hold more; measure bytes after escaping and split the payload.SharePoint Online rejects any REST request whose body exceeds 2,097,152 bytes (after JSON escaping, in UTF-8). A multi-line text field in a list holds far more than that – the cap is the request, not the field – so…
- Azure OpenAI 429 on a single request is a TPM ceiling, not a busy service — measure size, not frequencyAn Azure OpenAI 429 can mean one prompt exceeds the TPM ceiling and will never fit; a small and large request pair tells you which.One request whose prompt exceeds the deployment's TPM allowance can never fit the window, so retrying is futile — a sub-second rejection means "over the ceiling", not "busy"; separate the two with a small/large request…