#rest-api 139 articles
"File is missing" and "I cannot read the file" collapse into the same null — and the empty state then lies gotchas / rest-api A T | null helper folds 404, 403, 500 and network failure into one null, and the friendly empty state then lies to the one user who has overdue work; return {ok, status, data} and let only 404 (on every candidate path)… "Invalid text value" (HTTP 500) means a Text column hit its 255-character limit gotchas / rest-api Generic 500 naming no field; a Text column capped at 255 /_api/hubsites is trimmed per user — a lookup that walks the hubs finds your app for the page editor and not for the reader gotchas / rest-api GET /_api/hubsites returned 2 hubs to an admin and 1 to an ordinary user who had Full Control on the missing hub's site, so client code that discovers something by walking the hub list works for the editor and silently… /_api/HubSites returns an empty list (HTTP 200) when the caller cannot see the hub sites gotchas / rest-api The hub list is security-trimmed, so an account that cannot read the hub sites gets value: [] with HTTP 200; "this site has no hub" and "no inherited owner" must never be derived from it — read IsHubSite/HubSiteId from… AddValidateUpdateItemUsingPath answers 200 when it creates nothing — while a write to a missing column is a loud 400 gotchas / rest-api Field errors sit inside a 200 and no item is created; a write to a missing column is a 400 on every path, never a silent drop CreateCopyJobs with MoveButKeepSource keeps version history, timestamps and authorship — and how to actually prove it gotchas / rest-api The server-side Copy and Move job preserves all versions, Created/Modified and Created By/Modified By, leaves the source in place, and is callable from the browser under delegated auth; omitting AllowSchemaMismatch is… CreateCopyJobs: the library root is refused, progress logs are handed out once, and JobWarning is not a failure gotchas / rest-api Passing a document library's root folder URL to CreateCopyJobs fails with HTTP 500 "Null value for source item" while a subfolder URL copies the folder with its content; several file names containing the hash sign… ensureuser returns the login name — stop looking it up by Email gotchas / rest-api The response already carries LoginName; a follow-up siteusers filter on Email misses accounts whose UPN differs fields/getbyinternalnameortitle returns 400, not 404, for a missing field gotchas / rest-api It throws ArgumentException (HTTP 400), not 404; an existence-check that hard-fails on non-404 breaks the "field missing → create" path, and one that creates on any non-200 duplicates the column when throttled getfolderbyserverrelativeurl returns 200 for a folder that does not exist gotchas / rest-api It answers HTTP 200 with {"Exists": false} (and ListItemAllFields 200 with {"odata.null": true}), so a check on response.ok skips creating the folder and the next call 404s inside breakroleinheritance; measure the… Overwrite=true into a folder shared by many records silently replaces — and takes over — another record's file gotchas / rest-api Uploading with Files/AddUsingPath(…,Overwrite=true) under a user-supplied file name into a folder that holds documents of many records (cases, orders, tickets) lets a second file with the same generic name (such as… POST /views takes an SP.View body — ViewTypeKind fails, and @odata.type passes only under OData v4 gotchas / rest-api ViewTypeKind from the creation-information docs 400s, and an @odata.type annotation passes only with OData-Version: 4.0; measured recipe for view + scope + column order Promise.all discards the soft reads it was waiting for — one failing main read leaves every picker loading forever gotchas / spfx A failing main read rejects the batch and the soft reads' results and failure flags never reach the pickers; settle each read on its own StorageMetrics lies two ways — all zeros, or plausible numbers from an older state gotchas / rest-api zero = not calculated, but non-zero can be stale (261 files / 3.1 GB vs real 794 / 9.5 GB); trust it only when TotalFileCount exactly matches the files you counted $filter on a multi-value person field can return 400 — filter those client-side gotchas / rest-api Fall back to client filtering, but only on HTTP 400 A # or % in a file or folder name — the classic …Url APIs cannot reach it, and the old writes quietly save %25 gotchas / rest-api The classic …ServerRelativeUrl lookups, Files/add(url=), folders/add, moveto and copyto cannot reach it even encoded: 404/400, Exists: false for a folder that exists, and writes that answer 200 but save a literal %25… A $filter literal encoded twice finds nothing — with a 200 gotchas / rest-api If a helper already percent-encodes the quoted literal, wrapping the whole $filter in encodeURIComponent encodes the value a second time (/ → %252F, @ → %2540); SharePoint then compares against the escaped text and… A batch file move must be grouped by file, not by the record that points at it gotchas / rest-api When records share a file URL, iterating records moves it once and 404s on the rest; group by file, write the new URL to every record, and adopt a file already at the target on a rerun A column called MyField0 appears — a throttled existence check or two web parts provisioning at once gotchas / rest-api 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 conditional spread hides a phantom column until SharePoint returns 400 gotchas / spfx ...(cond ? {…} : {}) bypasses the excess-property check, so a field the list doesn't have reaches the POST and 400s; two scripted sweeps find the rest A copy job reports "finished" before the target shows every file – verify after the target settles, and read authors with $expand gotchas / rest-api After CreateCopyJobs reports JobState 0, the target's /items can still miss hundreds of files for a while; re-read until two reads match, and compare authors via $expand on /items, not siteusers A decimal comma erases a Number column — Number('7,25') is NaN, JSON sends null, SharePoint answers 204 gotchas / rest-api Number('7,25') is NaN, JSON.stringify sends null, SharePoint answers 204 and empties the column (measured: 7.25 → null on edit); parse text input comma-aware and reject NaN A deleted site and a site you cannot see are two different answers — read the status, not ok gotchas / rest-api _api/web on a non-existent site collection answers 404 (measured, no redirect), so if (!r.ok) return unknown makes a dead row in your own site inventory look like a site you merely lack permission on; keep r.status… A field you write is never read back — $select is an allowlist and a ?? fallback hides the gap gotchas / rest-api $select is an allowlist; a snapshot ?? live fallback silently erases the feature the snapshot exists for A file's time is not its item's time – a copy job carries the item time, and ListItemAllFields returns dates in the web's zone without "Z" gotchas / rest-api TimeCreated/TimeLastModified of a file can differ from the item's Created/Modified by seconds or years; copy jobs carry the item time, so compare items – and read them from /items(id) (UTC), because ListItemAllFields… A folder created with folders/AddUsingPath in a generic list has no list item — breaking its permissions returns 404 gotchas / rest-api In a custom list (BaseTemplate 100) web/folders/AddUsingPath creates a 'naked' folder: ListItemAllFields is odata.null (@odata.null under SPFx's OData v4) and breakroleinheritance is 404. Create list folders with… A folder's Files collection rejects a two-level $expand gotchas / rest-api ListItemAllFields/Editor 400s there; take ModifiedBy/Author from the file, and list one level through the folder API rather than a FileDirRef filter A formatter renders empty in the tab you are iterating in — verify in a fresh tab gotchas / lists After many REST writes the working tab shows even valid formatters blank while a fresh tab renders them; one tab per version, a scratch view, and simple formatters on fragile lists A Hyperlink column takes an object, not a string gotchas / rest-api It reads back as { Description, Url } ([object Object], React #31) and a plain string fails the whole save with 400; one helper each way, 255-character address limit, scheme allowlist A list with WriteSecurity 4 ignores Contribute — only ManageLists gets through gotchas / permissions Only ManageLists (Design/Edit/Full Control) overrides the item-level write block, so "break inheritance + give the group Contribute" grants nothing; resolve the role by RoleTypeKind, not by its localised name A list's ETag tracks its schema, so ItemCount comes back stale via 304 gotchas / rest-api Adding items never moves the list entity's ETag, so revalidation answers 304 and the browser serves the body from when the list was empty; affects entity reads (GetList, lists(guid)), not collections or /items A missing attachment returns 400, not 404 – an "is it there?" check by status code reads "not there" as a failure gotchas / rest-api AttachmentFiles('name') on a missing attachment returns 400 ArgumentOutOfRangeException; check existence from the attachment list A new Choice value in your provisioning code never reaches already-deployed sites gotchas / rest-api Create-if-missing never updates an existing field; a new Choice value in the manifest no-ops on deployed sites; reconcile with a post-hook SP.FieldChoice MERGE (plain JSON with @odata.type under SPHttpClient) that only… A page-size cap reported as a finding gotchas / rest-api 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 permission check that only asks "who has too much?" passes on an empty ACL gotchas / permissions "no principal outside the allow-list can write" is also satisfied when nobody can write at all; assert the opposite direction with a non-zero writer count, and read the principals strictly so a transient 429 cannot… A protocol-relative URL walks straight through your host allowlist gotchas / spfx A host check built on ^https?:// reads //evil.example/… as relative and lets it through; the browser then supplies the scheme and the request leaves for a foreign origin A provisioned list with ReadSecurity=2 looks perfect to an admin and empty to everyone else gotchas / lists ReadSecurity=2 looks perfect to an admin and empty to everyone else; WriteSecurity=2 breaks collaborative edits only A REST write over 2 MB fails with "The request message is too big" gotchas / rest-api 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… A setup marker that measures only part of the step freezes a half-provisioned site forever gotchas / spfx The success flag measured the id lookups, not the addroleassignment POST, so a 403 froze a site with groups that carry no permission level at all A SharePoint group created by code hides its own membership — and Full Control does not get you in gotchas / security sitegroups POST defaults to OnlyAllowMembersViewMembership: true, so only a member or the account that ran provisioning can read the members; Full Control does not help and identical permission masks prove it A silently-failed read turns reconciliation into delete-everything gotchas / rest-api A quiet if (!ok) break turns "sync what changed" into "delete everything"; fail-safe for a view means show what you can, for a delete it means do nothing A status code measures where the request stopped first, not what the user may do gotchas / permissions A probe that writes with a deliberately stale IF-MATCH and reads permissions off 403 vs 412 measures which gate the request hit first: SharePoint evaluates the etag BEFORE authorisation, so 412 comes back even where… A throttled permission probe must not downgrade — and cache — the role gotchas / rest-api 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 version is a full copy, not a delta — and a metadata-only edit costs one gotchas / lists The quota counts every version at full file size and a metadata-only edit costs one; measured on a live tenant, three column edits took a 1 MiB file to 4 MB Actions must go before the alias query string, not after the URL gotchas / rest-api Appending /breakroleinheritance(...) to a GetList(@u)?@u=... URL buries the action inside the parameter — splice it in before the ? Adding an attachment changes the item's ETag – the next MERGE with the old ETag fails with 412 gotchas / rest-api AttachmentFiles/add changes the item ETag; re-read before an IF-MATCH update An Entra group inside a SharePoint group is invisible to every membership check SharePoint offers gotchas / security SharePoint accepts an Entra group as a member of a site group, then never expands it: currentuser/groups is direct-only and sitegroups/{id}/users returns the group, not its people — resolve with Graph checkMemberGroups… An evidence record whose "who" is written by the browser proves nothing — compare it with the server-stamped Author, and compare on the sign-in name too gotchas / lists Append-only lists let any contributor add a row with someone else's name in a text column; Author is stamped by SharePoint. Compare the two — but against Author/Name (the sign-in claim) as well, or every user whose… An image upload that accepts image/* accepts SVG — and SVG is a script gotchas / security SVG is a script: inert inside , executed when the file's own URL is opened; allow-list raster MIME types An unencoded # in a $filter value cuts the URL — SharePoint gets half a query and answers 400 gotchas / rest-api The browser drops everything after # as a fragment, SharePoint gets an unterminated literal and answers 400; guest UPNs (#EXT#) always hit it; & cuts the expression and a raw + arrives as a space, so a valid literal… An unparseable JSON column is not an empty column — read-merge-write wipes the record gotchas / spfx catch → {} turns "could not read" into "is empty" and the next save writes the emptiness back over the record; keep three states, refuse the write before the first PATCH, lock the form visibly, and make recovery an… Breaking inheritance copies foreign Edit grants — and Edit defeats item-level security gotchas / security Another app's groups arrive with Edit, which carries ManageLists and bypasses item-level security; break without copying and prune the ACL — but only on a genuinely polluted scope, because copyRoleAssignments=false… Breaking inheritance without copying keeps only YOU — and the re-grant forgets custom groups gotchas / permissions copyRoleAssignments=false leaves exactly one role assignment (the caller), so re-granting Owners/Members/Visitors drops every direct grant and custom group; and an account that loses a configuration read can end up… Building a people picker in SPFx — the endpoints that actually work gotchas / spfx SP Search People source + ensureuser; why the obvious endpoints fail Calling SharePoint REST like a pro guides The client landscape, headers that matter, safe writes, reading well, field/list creation quirks, and a ten-minute diagnosis routine Check another user's effective permissions (without their password) snippets / rest Verify what a normal user can actually reach — from your own session, no test account; mind Open = Low bit 16 Check-then-insert races produce duplicate rows — and "keep the lowest Id" dedup deletes the wrong one gotchas / rest-api GET-check then POST-insert without a unique column races into double inserts; dedup on read by version, never delete "lowest Id" Choice fields accept any value over REST — validation is a form-only illusion gotchas / rest-api REST skips choice validation entirely; enforce vocabulary yourself Copy jobs fed with file URIs: the folders you pre-create carry your account and today's date – and the TaxCatchAll warning is just image tags gotchas / rest-api A CreateCopyJobs job that gets folder URIs carries folder metadata, a job fed with file URIs needs existing target folders, and folders created with AddUsingPath show the running account and the creation time until you… Counting a site's audience from its associated groups silently misses nested groups gotchas / permissions Owners/Members/Visitors often contain Everyone except external users or the Microsoft 365 group claim (PrincipalType 4); keeping only PrincipalType 1 rows turns a whole company into one or two people – treat any nested… Created/Modified on a document library won't stick — and the API says 200 while ignoring you gotchas / lists A plain MERGE reports 204 and SharePoint overwrites both with "now"; ValidateUpdateListItem + bNewDocumentUpdate is the only path that holds, it wants the web's locale format (the error message tells you which), reads… createfieldasxml ignores Name/StaticName — the internal name is derived from DisplayName gotchas / lists createfieldasxml on a list ignores Name/StaticName unless you pass Options: 8 (AddFieldInternalNameHint); otherwise the internal name is escaped from the label – create with ASCII and rename, or use the hint to keep a… Creating a modern page via REST is a three-step dance, not one POST gotchas / rest-api CanvasContent1 won't stick on create; create → SavePageAsDraft → Publish, canvas is JSON; plain JSON is enough, Description needs its own write and a re-publish; the file name is random — rename with moveto DateTime fields — write full ISO with a time zone, derive the day locally gotchas / rest-api No-timezone writes 400; UTC reads shift the day; read a day in the zone it was written in — including noon-UTC dates at UTC+12 to +14; a bare date in $filter is UTC midnight, so le drops the last day of a range Deduplicating events from an external API with a timestamp watermark silently drops simultaneous and late events gotchas / rest-api When you pull activity events (opens, clicks, audit entries) in batches with an overlapping cursor, "skip anything not newer than the last counted event" throws away a second event with the same second-precision… Drop __metadata from write bodies — odata=verbose needs OData v3, and SPHttpClient sends v4 gotchas / rest-api Old-tutorial payloads 400 as plain JSON, and switching to odata=verbose 400s again under SPHttpClient, which sends odata-version: 4.0; write plain JSON without the type hint, or blank odata-version where the verbose… Duplicate trimming hides exactly the file copies you are searching for gotchas / search Search collapses identical content by default, so a copy-finder returns "no copies" exactly when perfect copies exist; trimduplicates=false on duplicate-hunting queries only encodeURIComponent won't save you from apostrophes in OData literals gotchas / rest-api encodeURIComponent leaves ' alone; double it Enumerate every site from the browser console (no admin API, no tooling) gotchas / search contentclass:STS_Site OR STS_Web over Search REST lists every web the account can read, no admin role or tooling; plus the traps of a cross-site F12 sweep (Hidden filter, case-sensitive GetList, 400 vs 404, per-web… FieldValuesAsText re-encodes underscores in JSON keys gotchas / rest-api A_x0020_B comes back as A_x005f_x0020_x005f_B; decode _x005f_ before matching File size in a document library — File_x0020_Size 400s; use $expand=File gotchas / rest-api File_x0020_Size 400s in $select; use File/Length File upload via /Files/add: nometadata works under OData v4 — an empty OData-Version breaks SPFx when you leave out Content-Type gotchas / rest-api nometadata + octet-stream under the default OData v4; an empty OData-Version without Content-Type throws inside SPHttpClient; plus the empty-filename mobile camera trap File versions come back oldest-first, so $top truncates the newest ones gotchas / rest-api /Versions is ascending, so $top drops the NEWEST ones; sorting the result descending afterwards hides it; $orderby=ID desc IS honoured together with $top (measured), and the list never contains the current version Find externally / anonymously shared content via Search snippets / rest Oversharing / Copilot-readiness audit: ViewableByExternalUsers:1 surfaces files shared out, security-trimmed, one query FirstUniqueAncestorSecurableObject has no type in odata=nometadata — tell web, list and item apart by the shape of the answer gotchas / permissions The endpoint returns a web, a list or a list item depending on where inheritance breaks, and with odata=nometadata nothing names the type; classify by properties (FileSystemObjectType = item, BaseTemplate = list… Gallery cards render from tileProps, not from formatter gotchas / lists The documented top-level formatter is ignored; the card lives in a nested, undocumented tileProps.formatter. Layout and card must go in two separate writes (PnP or REST/SPFx) Get lists by URL, not by title gotchas / rest-api getbytitle() breaks the moment someone renames a list; resolve by URL (bonus: GetList normalises folder paths to their list) GetObjectSharingInformation — GET always 405, CreatedBy always null, and other measured facts gotchas / rest-api GET is 405 every time (stop retrying it: 2.7× slower), CreatedBy is always null, $expand is dead weight GetStorageEntity returns 200 for a missing key, not 404 gotchas / rest-api Unset tenant property answers 200 {"odata.null":true}; "unset" and "failed" are indistinguishable by status code, and writing needs Tenant Admin — same trap in …?$select=Exists, which returns 200 {"Exists":false} GetUserEffectivePermissions ignores the user's Entra security groups — and Full Control's effective mask is not the role's mask gotchas / permissions Asked on behalf of another user, GetUserEffectivePermissions counts Microsoft 365 group membership but not Entra security group membership — directly assigned or nested in a SharePoint group, still after 40 s — so it… getUserEffectivePermissions returns a 64-bit mask — decode it wrong by one bit and every user looks locked out gotchas / security ViewListItems is bit 0, Low/High are decimal strings; decode off by one and every ordinary member looks locked out. Sanity-check the decoder against a site admin AND a known Read user HasUniqueRoleAssignments proves the break, not the hardening — and a half-done break is worse than inheritance gotchas / permissions The flag is true after the break alone, so verifying on it reports "hardened" over a list whose trim never ran; with the safe copy=true form that state is worse than inheritance. Read roleassignments and measure… Hiding a field in an SPFx web part is not a permission gotchas / security Role-based UI field hiding is cosmetic; Read on the list = REST/Export/other web parts see it; confidentiality needs a separate list, item perms, or a server tier — plus the two traps the separate list creates: a… Item-level edit rights cover every field – don't trust status, approver or link fields the user can write gotchas / permissions Unique permissions on an item don't separate fields: whoever may edit it can set Status, Approver or a lookup Id via REST – derive trust from admin-only data and version history JoinHubSite silently moves a site that already belongs to another hub gotchas / rest-api Calling JoinHubSite on a site that sits in hub A returns 200 and the site is in hub B at once — no unjoin step, no warning, so any "apply hub" plan must read the current hub first Limited Access cannot be granted — the API returns 200 and does nothing gotchas / permissions RoleTypeKind: 1 is derived, not assignable: SharePoint maintains it so people can traverse to a child with unique permissions, and an explicit addroleassignment returns 200 while doing nothing. Strip it from EVERY… List item IDs are never reused after a purge — but a recreated list starts at 1 again gotchas / lists Purging a list does not rewind its counter, so orphaned rows in a derived list are dead weight; a recreated list restarts at 1 and turns them into wrong-item attributions List item versions come back newest-first and include the current one – the opposite of file versions gotchas / rest-api items(id)/versions is DESCENDING and its first row is the current version (IsCurrentVersion: true) – the opposite of file /Versions; Created on a version is UTC WITHOUT the Z, so new Date() shifts it by the browser… Locking down a no-script site from code: web NoCrawl is denied even to a site collection admin, and turning off access requests returns 200 and changes nothing gotchas / security On a modern site with custom scripts disabled, NoCrawl on the WEB returns 403 via REST and CSOM even for a site collection admin, and SetUseAccessRequestDefaultAndUpdate(false) returns 200 without effect; both work… Looking up a field that does not exist returns 400, not 404 gotchas / rest-api fields/GetByInternalNameOrTitle on a missing field answers 400; test existence with a $filter instead Lookup and person fields — read with $expand, write with <Name>Id gotchas / rest-api Read via $expand + projections, write via Id; ~12-lookup query limit Migrating list items with original authors, dates — and their version history gotchas / lists Copy and Move does not do lists, but REST does the job item by item — AddValidateUpdateItemUsingPath takes Author, Editor, Created and Modified at creation (one call, one version), version history can be replayed with… moveto succeeds and your code reports a failure — action endpoints return an empty body gotchas / rest-api Action endpoints (moveto, removebyid) return an empty body, so response.json() throws after the operation happened and invites a retry; decide by status, parse only what you read Multi-line text in a document library stops at 255 characters gotchas / lists A REST MERGE stores 70k characters, but ValidateUpdateListItem rejects anything over 255 unless the column has UnlimitedLengthInDocumentLibrary – set it at creation or PATCH it later Narrowing a principal to Read — "already reads" checked before stripping write leaves it with nothing gotchas / permissions Edit and Contribute contain the View bit, so a "does it already read?" check passes, Read is never added, and the step that removes the write binding then removes the only binding that carried read access — the… Per-user data vanishes for admin accounts: pageContext.user.email is empty and eq '' matches nothing gotchas / spfx Mailbox-less accounts get an empty pageContext.user.email; rows save under an empty key, SharePoint stores empty text as NULL, and $filter=Field eq '' never matches it again PermissionKind is 1-based — decoding BasePermissions with 1 << kind invents a security finding gotchas / permissions The flag for kind k is 1 << (k - 1), so 1 << k invents a security finding: Contribute appears to hold ManageLists (it holds ViewFormPages). Calibrate any decoder against a known level first, and note the tell — a… POST /fields rejects a subtype property like RichText unless the body names the subtype gotchas / rest-api { FieldTypeKind: 3, RichText: false } without @odata.type is read as SP.Field and fails with 400 – plain text columns in the same loop succeed, so the list ends up half-built; use CreateFieldAsXml or annotate the type Power Platform admin API returns nextLink on a regional host – a guard that only knows api.powerapps.com breaks paging for US tenants gotchas / rest-api Admin listings page through unitedstates.api.powerapps.com (and other one-label regional hosts); a token guard that allows only the global host rejects page two, and you never see it on a small tenant – allow exactly… Provisioning folder trees at scale in SharePoint Online guides What creating 86 folders and breaking inheritance really costs (no throttling), the alias-parameter syntax, reading a whole tree with one item query (two calls) instead of ninety, and copyRoleAssignments=false silently… Putting an Image web part on a modern page via REST — the control JSON that renders, with a link gotchas / rest-api Out-of-the-box Image web part (d1d91016-…) in CanvasContent1: file ids in properties + customMetadata, the URL in serverProcessedContent.imageSources, the click target in links.linkUrl; works in a full-width section… Read all items from a large list — paging done right snippets / rest 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 RecycleByID sends a file version to the recycle bin — DeleteByID does not gotchas / rest-api The two removal methods on /Versions differ: permanent vs. site recycle bin (ItemType 2, restorable); measured, with vid = major*512 + minor Reindex a library from code works even on a no-script site — the root folder's property bag is writable, the web's is not gotchas / rest-api Bumping vti_searchversion on the library's RootFolder property bag via CSOM succeeds on sites with custom scripts disabled (unlike the web property bag); find the library from any item URL with GetWebUrlFromPageUrl +… REST DELETE on a list item is permanent — recycle() is the bin gotchas / rest-api The usual delete call skips BOTH recycle bins; /items(id)/recycle() is what a “restorable for 93 days” promise needs Restoring inheritance is a one-way door — snapshot the unique assignments first gotchas / permissions resetroleinheritance throws away every unique role assignment and SharePoint keeps no copy, so an "undo" is only possible if the code saved the assignments BEFORE the call; re-breaking with copyRoleAssignments=true… Retrying a throttled call is safe for GET only gotchas / rest-api 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) Retrying a write without the "new" columns on any 400 silently loses data — drop only the column the error names gotchas / rest-api When one new column is missing, a group-wide retry drops the ones that exist too; drop only the fields the error names as not existing, loop, and tell the user what was left out Reverse-engineering Plumsail Forms apps for migration or audit guides Where the JSON definitions, custom JS, and hosting web parts live; a read-only extraction routine and the client-side patterns (dummy-item pages, UI-only security, race conditions) to flag before a migration Search ignores unknown managed properties — silently gotchas / search A made-up property name returns HTTP 200 with full results; auto-created Choice properties are not queryable; probe with a fake name, map to RefinableString Search ParentLink points at a library view, not at the library gotchas / search Often …/Forms/AllItems.aspx, so used as a path it empties a Path: filter and can send a save into Forms while a GetList probe still passes; normalize on read and on write Search queries that actually work guides The one mandatory header, practical KQL, managed properties (RefinableString*), paging/sorting, and the freshness/trimming traps Search REST API needs odata-version 3.0 gotchas / rest-api The header behind mysterious search 500s Search REST returns 500 "unknown error" and 429 "low resources" for minutes at a time — retry the read, don't fix the query gotchas / search SharePoint Search REST can fail for a few minutes with HTTP 500 SearchServiceException (no Retry-After) and HTTP 429 low-resources errors; the same query succeeds seconds later, so retry reads briefly with a fixed… SharePoint REST has no increment — a "+1" computed from the count you read at page load loses every vote cast in between gotchas / rest-api A MERGE writes a value, so count + 1 from what the page loaded, sent with IF-MATCH: *, overwrites every vote cast since that page load; count distinct AuthorIds of per-user rows, or read fresh and write with the item's… SharePoint REST vs Microsoft Graph — which API for which job guides A decision table by capability, the SPFx auth difference, common wrong picks, and throttling budgets SharePoint's built-in PDF conversion endpoint works only with a plain fetch — auth headers break the redirect gotchas / rest-api _api/v2.0/drives/…/content?format=pdf converts Office files server-side with no Graph consent, but auth-decorating clients break its 302 to *.svc.ms; resolve the drive explicitly and guard on Content-Type Silent catch-to-empty fallbacks + destructive writes = data loss gotchas / rest-api catch → [] + delete-then-insert = data loss; strict and safe reads (+ a settings read that hides its failure lets a full-object save overwrite the config with defaults) SP.WebProxy is add-in-only — from SPFx it 403s with "without an app context" gotchas / spfx No SharePoint-native CORS proxy for SPFx; the 403 hides inside an HTTP 200 SPFx in Teams — the current user's numeric ID belongs to the host site, not to your data site gotchas / spfx legacyPageContext.userId in a Teams personal app is the tenant root site's ID; written as the user's ID on another site it can silently point to someone else Telling a list's own columns from inherited ones — Hidden is not enough gotchas / rest-api Hidden is not enough; FromBaseType + CanBeDeleted are what separate the list's own columns The 5,000-item list view threshold — and why indexes must come early gotchas / lists It's scanned rows, not returned rows; index early, page always The browser cache answers your read-after-write check — with a 200 gotchas / rest-api The same URL read on both sides of a write is served from the browser cache, so res.ok guards pass on stale state; a cached existence check makes POST /fields create a duplicate column and report success, and an… The ETag key is @odata.etag in OData v4 – reading odata.etag silently turns IF-MATCH into * gotchas / rest-api SPHttpClient speaks OData v4: the item ETag comes as @odata.etag; reading odata.etag yields undefined and your IF-MATCH protection quietly disappears The tenant root site is not necessarily readable by everyone gotchas / permissions Measured: unique permissions, no Everyone except external users, an ordinary employee's mask High=0, Low=0; don't anchor a must-work-for-everyone mechanism there, measure with getusereffectivepermissions There is no sitegroups/removebyname — delete a group by Id gotchas / rest-api GroupCollection has GetByName but only RemoveById; resolve the Id first, then delete; verify with getbyname → 404 Title and Created are the fields you filter on — and the ones your provisioning code never indexes gotchas / lists 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 Turn a library URL someone pasted into web + library + folder snippets / rest SP.Web.GetWebUrlFromPageUrl, called on the CURRENT web, resolves the web of any URL in the tenant (other site collections too, 404 for a missing site); strip Forms/…aspx, prefer the ?id= folder, take the first segment… Two MERGEs to the same list item at once: the second one fails with 409 Save Conflict even with IF-MATCH: * – and a fire-and-forget write just disappears gotchas / rest-api IF-MATCH: * only switches off the ETag check (412); SharePoint still refuses a second save of the same item while the first is in flight with 409 Save Conflict. Queue writes per item (list#id), retry 409 briefly, never… Unbounded Promise.all fan-out over a customer-sized collection invites HTTP 429 gotchas / rest-api Fan-out is safe when YOUR code decides the count, dangerous when the customer's data does; bounded batch and worker-pool patterns Upload a generated image as a list item attachment (no file picker) snippets / rest Canvas charts, placeholder banners, QR codes — AttachmentFiles/add takes a raw ArrayBuffer; the item must exist first Uploaded .html grows after you set column values — SharePoint injects mso document properties (and rewrites Office files on upload) gotchas / rest-api Setting column values rewrites the file; hash read-back fails for markup, holds for PDF; Office packages change on upload itself… View counts and ratings written by readers cannot live in a list only editors may write gotchas / lists Views and ratings are written by the reader, so on a WriteSecurity: 4 content list every click is a swallowed 403 while the UI says thanks View formatting JSON can't contain < or & — XmlException on save gotchas / lists It's stored inside the view's schema XML; reverse the comparison and nest if() View formatting lands on the wrong view — and a missing column is not an empty string gotchas / lists Site Pages has no AllItems.aspx and the default view may be grouped; a column missing from the view is undefined, so the empty-check guard never hides it Web part properties are missing from CanvasContent1 on single-part app pages gotchas / rest-api Single-part app pages store web part config elsewhere; do not diagnose "not set" from page fields — but the web part id IS there, so use it to find the hosting page (not ClientSideApplicationId) webUrl + a server-relative URL doubles the site path gotchas / rest-api Survives for months as a display value or grouping key and breaks the first time it becomes a filter; build object URLs from the origin in one helper Write to Azure Table Storage from a Node Azure Function — no SDK, SharedKeyLite snippets / rest Telemetry/counters from an Azure Function without @azure/data-tables bloat — Table-flavored SharedKeyLite signing, first-= connection-string parsing, 409 = fine X-RequestDigest expires ~30 min after page load — but adding it by hand in SPFx makes things worse gotchas / rest-api Writes 403 "security validation is invalid" on a long-open page; raw fetch needs a fresh digest from /_api/contextinfo, but in SPFx a hand-set header switches SPHttpClient's own digest and 403-recovery OFF