ποΈ Vault Index
Every single thing in the vault, on one page. Section names link to folder READMEs; leaves link straight to the content.
Last updated: 2026-11-14
- π§° scripts/ β PowerShell scripts with comment-based help, read-only unless stated
- Terminal animations β the writing scripts as an animated PowerShell console; plain SVG, no JavaScript, respects
prefers-reduced-motion - What the scripts actually print β console + CSV samples for every script from real runs, including what a denied read looks like
- reporting/
- Get-SiteCollectionInventory.ps1 β every site collection in one CSV: storage, owner, template, sharing, lock state, last activity
- Get-HubSiteStructure.ps1 β the whole hub topology; re-reads sites individually rather than reporting "no associations" when the bulk call omits
HubSiteId - Get-InactiveSitesReport.ps1 β dormant sites ranked by days since last change, with the storage they hold
- Get-TenantSettingsBaseline.ps1 β tenant settings to JSON, then
-CompareWithprints "was X, now Y" per property - Get-AppCatalogInventory.ps1 β catalog version vs. the version each site actually runs; a new
.sppkgupdates nobody by itself
- permissions/
- Get-ExternalSharingReport.ps1 β sharing capability per site plus every guest;
Get-SPOExternalUsercaps-PageSizeat 50 and stops early on some tenants - Get-UniquePermissionsReport.ps1 β broken inheritance at web, list and opt-in item level; a capped item scan is labelled PARTIAL, never reported as complete
- Get-SharingLinksReport.ps1 β the hidden
SharingLinks.<doc>.<kind>.<link>groups: who used which link, and the document behind it - Get-EveryoneClaimReport.ps1 β where the tenant-wide claims were granted, including buried inside an ordinary SharePoint group
- Get-SiteCollectionAdminReport.ps1 β the privilege that bypasses every item-level break and never shows up in Owners
- Set-SiteSharingCapability.ps1 β β οΈ writes: external sharing on/off per site or tenant-wide; the tenant setting is a ceiling and the check ranks levels explicitly, because the enum's numbers do not follow its own hierarchy
- Set-SiteDefaultLinkPermission.ps1 β β οΈ writes: the permission pre-selected in the Share dialog; setting View does not forbid Edit, it stops Edit being what happens by default
- Set-SiteAccessRequestSettings.ps1 β β οΈ writes: the per-site Access Requests Settings dialog end to end; two of its settings have no cmdlet at all and need CSOM, and a tenant override can make one of them cosmetic
- Get-ExternalSharingReport.ps1 β sharing capability per site plus every guest;
- lists-and-libraries/
- Get-LargeListsReport.ps1 β lists approaching or past the 5,000-item view threshold
- Get-ListInventory.ps1 β every list with versioning, content types and unique permissions;
MajorVersionLimit = 0is spelled out as unlimited, not printed as zero - Get-ListIndexAdvice.ps1 β big lists with no indexed column, and how many of the 20 index slots remain
- Get-CheckedOutFilesReport.ps1 β server-side CAML on
CheckoutUser; flags files with no checked-in version that nobody else can see - Get-ContentTypeUsage.ps1 β matches list copies by content type Id prefix, which name matching never does reliably
- Get-LongFileUrlReport.ps1 β the ~400-char path and 255-char name limits, plus the characters that break sync clients
- cleanup/
- Get-FileVersionBloatReport.ps1 β what version history really costs, per library and per file; unreadable history is skipped, not counted as zero
- Remove-ExcessFileVersions.ps1 β β οΈ writes: trims to the newest N versions,
-WhatIfsupported; trimmed versions go to the site recycle bin by default (RecycleByID),-Permanentdeletes them for good - Get-RecycleBinReport.ps1 β both stages, grouped by who deleted what, with the 93-day purge countdown
- Get-DeletedSitesReport.ps1 β deleted site collections still burning quota, and the restore-or-lose deadline
- Get-DuplicateFilesReport.ps1 β duplicates by name + byte size through the index;
TrimDuplicatesoff, and it aborts rather than call aSize-less result set clean
- search/
- Test-SearchManagedProperty.ps1 β exists? has data? filterable? β settled via
sortlist, the one probe Search answers honestly, plus a fabricated control name
- Test-SearchManagedProperty.ps1 β exists? has data? filterable? β settled via
- Terminal animations β the writing scripts as an animated PowerShell console; plain SVG, no JavaScript, respects
- π₯ gotchas/ β real-world traps as symptom β cause β fix
- rest-api/
- Unbounded
Promise.allfan-out invites 429 β Fan-out is safe when YOUR code decides the count, dangerous when the customer's data does; bounded batch and worker-pool patterns POST /viewstakes anSP.Viewbody βViewTypeKindfrom the creation-information docs 400s, and an@odata.typeannotation passes only withOData-Version: 4.0; measured recipe for view + scope + column order- Get lists by URL, not by title β
getbytitle()breaks the moment someone renames a list; resolve by URL (bonus:GetListnormalises folder paths to their list) - REST
DELETEon an item is permanent β The usual delete call skips BOTH recycle bins;/items(id)/recycle()is what a βrestorable for 93 daysβ promise needs - Actions go before the alias query string β Appending
/breakroleinheritance(...)to aGetList(@u)?@u=...URL buries the action inside the parameter β splice it in before the? - FieldValuesAsText re-encodes underscores in JSON keys β
A_x0020_Bcomes back asA_x005f_x0020_x005f_B; decode_x005f_before matching GetObjectSharingInformationtraps β GET is 405 every time (stop retrying it: 2.7Γ slower),CreatedByis always null,$expandis dead weight- Search REST needs
odata-version: 3.0β The header behind mysterious search 500s - DateTime: write full ISO, derive days locally β 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
ledrops the last day of a range - Drop
__metadatafrom write bodies β Old-tutorial payloads 400 as plain JSON, and switching toodata=verbose400s again underSPHttpClient, which sendsodata-version: 4.0; write plain JSON without the type hint, or blankodata-versionwhere the verbose form is really needed - File upload via
/Files/add: nometadata works, an emptyOData-Versionbreaks SPFx βnometadata+ octet-stream under the default OData v4; an emptyOData-VersionwithoutContent-Typethrows insideSPHttpClient; plus the empty-filename mobile camera trap AddValidateUpdateItemUsingPathanswers 200 when it creates nothing β 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- Apostrophes in OData literals β
encodeURIComponentleaves'alone; double it - An unencoded
#in a$filtervalue cuts the URL β 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 silently matches nothing (measured) βencodeURIComponent(odataString(x)) - A
#or%in a file or folder name β The classicβ¦ServerRelativeUrllookups,Files/add(url=),folders/add,movetoandcopytocannot reach it even encoded: 404/400,Exists: falsefor a folder that exists, and writes that answer 200 but save a literal%25; ResourcePath API with the path doubled and URL-encoded, plus its different answers on an existing target (measured) ensureuserreturns the login name β The response already carriesLoginName; a follow-upsiteusersfilter onEmailmisses accounts whose UPN differs- Choice fields accept any value β REST skips choice validation entirely; enforce vocabulary yourself
- A decimal comma erases a Number column β
Number('7,25')is NaN,JSON.stringifysendsnull, SharePoint answers 204 and empties the column (measured: 7.25 β null on edit); parse text input comma-aware and reject NaN - Lookup fields need
$expandβ Read via$expand+ projections, write via<Name>Id; ~12-lookup query limit - File size needs
$expand=FileβFile_x0020_Size400s in$select; useFile/Length - Uploaded
.htmlgains mso document properties; Office files are rewritten on upload β Setting column values rewrites the file; hash read-back fails for markup, holds for PDF; Office packages change on upload itselfβ¦ - βInvalid text valueβ is a MaxLength 500 β Generic 500 naming no field; a
Textcolumn capped at 255 - Create a modern page via REST (3-step) β
CanvasContent1won't stick on create; create β SavePageAsDraft β Publish, canvas is JSON; plain JSON is enough,Descriptionneeds its own write and a re-publish; the file name is random β rename with moveto $filteron multi-value person fields 400s β Fall back to client filtering, but only on HTTP 400GetStorageEntityreturns 200 for a missing key β Unset tenant property answers200 {"odata.null":true}; "unset" and "failed" are indistinguishable by status code, and writing needs Tenant Admin β same trap inβ¦?$select=Exists, which returns200 {"Exists":false}- Silent read failure drives delete-all β A quiet
if (!ok) breakturns "sync what changed" into "delete everything"; fail-safe for a view means show what you can, for a delete it means do nothing - Batch file move: group by file, not by record β 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
- "File is missing" vs. "I cannot read it" β A
T | nullhelper 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) mean empty /_api/HubSitesreturns an empty list, not 403 β The hub list is security-trimmed, so an account that cannot read the hub sites getsvalue: []with HTTP 200; "this site has no hub" and "no inherited owner" must never be derived from it β readIsHubSite/HubSiteIdfrom_api/siteand treat "belongs to a hub that is not in the list" as unknown- A deleted site vs. a site you cannot see β
_api/webon a non-existent site collection answers 404 (measured, no redirect), soif (!r.ok) return unknownmakes a dead row in your own site inventory look like a site you merely lack permission on; keepr.status, treat 404 as a defect in YOUR list (with a fix), and stay silent about everything else - App page properties are not in CanvasContent1 β 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)
- Field written but missing from
$selectβ$selectis an allowlist; asnapshot ?? livefallback silently erases the feature the snapshot exists for - Silent fallbacks poison destructive writes β
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) - Check-then-insert races produce duplicate rows β GET-check then POST-insert without a unique column races into double inserts; dedup on read by version, never delete "lowest Id"
- SharePoint REST has no increment β A MERGE writes a value, so
count + 1from what the page loaded, sent withIF-MATCH: *, overwrites every vote cast since that page load; count distinctAuthorIds of per-user rows, or read fresh and write with the item's ETag - The browser cache answers your read-after-write check β with a 200 β The same URL read on both sides of a write is served from the browser cache, so
res.okguards pass on stale state; a cached existence check makesPOST /fieldscreate a duplicate column and report success, and an append-only number allocator loses every round (check for double-encoded filters too); bust only reads on the write path - A list's ETag tracks its schema, so
ItemCountcomes back stale via 304 β 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 - Provisioning skips schema changes to existing fields β 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.FieldChoiceMERGE (plain JSON with@odata.typeunderSPHttpClient) that only ever widens the set βChoicesis replace, so a failed read of the current values turns the union into the manifest and deletes the rest, while items keep the values the definition no longer knows X-RequestDigestexpires mid-session β Writes 403 "security validation is invalid" on a long-open page; rawfetchneeds a fresh digest from/_api/contextinfo, but in SPFx a hand-set header switchesSPHttpClient's own digest and 403-recovery OFF- PDF conversion endpoint: plain fetch only β
_api/v2.0/drives/β¦/content?format=pdfconverts 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 - Don't cache a throttled permission probe β A 429/403 on
currentuser/groupsresolves to the lowest role; cache it and the user is stuck read-only for the TTL; only persist a confirmed (200) result - There is no
sitegroups/removebynameβGroupCollectionhasGetByNamebut onlyRemoveById; resolve the Id first, then delete; verify withgetbynameβ 404 - Telling a list's own columns from inherited ones β
Hiddenis not enough;FromBaseType+CanBeDeletedare what separate the list's own columns - A page-size cap reported as a finding β A round number (100 / 500 / 999 / 5000) is the tell; the limit of the request presented as a measurement of the tenant, then cached
- File versions come back oldest-first β
/Versionsis ascending, so$topdrops the NEWEST ones; sorting the result descending afterwards hides it;$orderby=ID descIS honoured together with$top(measured), and the list never contains the current version RecycleByIDsends a file version to the recycle bin βDeleteByIDdoes not β The two removal methods on/Versionsdiffer: permanent vs. site recycle bin (ItemType 2, restorable); measured, withvid = major*512 + minor- Retrying a throttled call is safe for GET only β 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) fields/getbyinternalnameortitle400s for a missing field β It throwsArgumentException(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- A column called
MyField0appears βPOST /fieldswith 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 getfolderbyserverrelativeurl200s for a missing folder β It answers HTTP 200 with{"Exists": false}(andListItemAllFields200 with{"odata.null": true}), so a check onresponse.okskips creating the folder and the next call 404s insidebreakroleinheritance; measure the value, keep three states, and use the path-based lookup β the classic one saysExists: falseeven for an existing folder with#or%in its name- A Hyperlink column takes an object, not a string β 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 webUrl + serverRelativeUrldoubles the site path β 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- A folder's
Filescollection rejects a two-level$expandβListItemAllFields/Editor400s there; takeModifiedBy/Authorfrom the file, and list one level through the folder API rather than aFileDirReffilter movetosucceeds and your code reports a failure β Action endpoints (moveto,removebyid) return an empty body, soresponse.json()throws after the operation happened and invites a retry; decide by status, parse only what you readOverwrite=truein a shared folder steals another record's file β Uploading withFiles/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 asimage.jpg) replace the first record's file β SharePoint keeps the same list item and adds a new version β and the metadata update that follows then re-links that item to the second record. Put the record key into the file name (or give every record its own folder), and add it when moving older files- Event dedup by timestamp watermark loses events β 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 timestamp and any late-arriving event with an older timestamp β exactly the ones the overlap exists to catch; dedupe inside the overlap by event identity (action + timestamp + occurrence count) and let the watermark decide only what lies outside it
- Image web part via REST (canvas JSON) β Out-of-the-box Image web part (
d1d91016-β¦) inCanvasContent1: file ids inproperties+customMetadata, the URL inserverProcessedContent.imageSources, the click target inlinks.linkUrl; works in a full-width section and in columns - Missing field lookup = 400, not 404 β
fields/GetByInternalNameOrTitleon a missing field answers 400; test existence with a$filterinstead - Attachment add changes the ETag β
AttachmentFiles/addchanges the item ETag; re-read before anIF-MATCHupdate - ETag key is
@odata.etagβ SPHttpClient speaks OData v4: the item ETag comes as@odata.etag; readingodata.etagyields undefined and your IF-MATCH protection quietly disappears - Copy and Move keeps versions, timestamps and authors β 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 an immediate 500, a lower version limit on the destination truncates history after the transfer, and β measured β a plain CopyTo loses the version history but keeps the author
- Copy job: library root, one-shot logs, harmless warnings β 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 break a multi-URI job with HTTP 400 unless it is sent as %23; GetCopyJobProgress returns only the log entries queued since the previous call, a job emits several JobStart/JobEnd pairs so JobEnd alone is not completion (JobState 0 is), a successful copy can carry a JobWarning about label based policy, ListItemAllFields refuses $expand=Author, and item-level unique permissions are not copied (IncludeItemPermissions fails with HTTP 500 on a copy) but can be re-applied afterwards without touching Modified, every job must take URIs from one parent folder into a destination that already exists, and NameConflictBehavior 0 silently skips an existing file when the same job also carries a new one, so the conflict count must come from the destination, not from the log
- JoinHubSite moves sites between hubs β Calling
JoinHubSiteon 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 - REST request body is capped at 2 MB β 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 measure body bytes and split large JSON into parts.
- Reindex a library from code under NoScript β Bumping
vti_searchversionon 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 withGetWebUrlFromPageUrl+web/GetList - Missing attachment is 400, not 404 β
AttachmentFiles('name')on a missing attachment returns 400 ArgumentOutOfRangeException; check existence from the attachment list - POST /fields rejects a subtype property like RichText unless the body names the subtype β { 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
- Copy job ends before the target is visible β 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
- Copy job by files: folder metadata is lost β 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 rewrite Created/Modified/Author/Editor with ValidateUpdateListItem; the JobWarning 'Field TaxCatchAll cannot be found' comes from the automatic Image Tags column
- File time vs. item time, and ListItemAllFields dates β 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 returns web-local time without Z
- /_api/hubsites is trimmed per user β
GET /_api/hubsitesreturned 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 fails for readers; resolve it at authoring time and store the result in the web part properties - StorageMetrics can be stale, not just zero β zero = not calculated, but non-zero can be stale (261 files / 3.1 GB vs real 794 / 9.5 GB); trust it only when
TotalFileCountexactly matches the files you counted - Concurrent MERGE to one item: 409 β
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 retry 412, and do not let a state-carrying write fail silently - List item versions are newest-first β
items(id)/versionsis DESCENDING and its first row is the current version (IsCurrentVersion: true) β the opposite of file/Versions;Createdon a version is UTC WITHOUT theZ, sonew Date()shifts it by the browser offset β useModified;VersionId = major*512 + minor - Power Platform admin API nextLink goes to a regional host β 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 one DNS label in front of the three admin hosts, nothing else - Retrying without new columns on any 400 loses data β 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
folders/AddUsingPathin a generic list makes a folder without a list item β In a custom list (BaseTemplate 100)web/folders/AddUsingPathcreates a 'naked' folder:ListItemAllFieldsisodata.null(@odata.nullunder SPFx's OData v4) andbreakroleinheritanceis 404. Create list folders withAddValidateUpdateItemUsingPathandUnderlyingObjectType: 1, then check the item state, not justExists- A
$filterliteral encoded twice finds nothing β with a 200 β If a helper already percent-encodes the quoted literal, wrapping the whole$filterinencodeURIComponentencodes the value a second time (/β%252F,@β%2540); SharePoint then compares against the escaped text and returns an empty result, no error; encode in exactly one layer and test with a fake server that decodes once and really filters
- Unbounded
- lists/
- The 5,000-item view threshold β It's scanned rows, not returned rows; index early, page always
- View formatting JSON can't contain
<or&β It's stored inside the view's schema XML; reverse the comparison and nestif() - An empty Date field is not
''β Blank dates fall through to "overdue" and turn rows red; test.displayValue - Gallery cards render from
tilePropsβ The documented top-levelformatteris ignored; the card lives in a nested, undocumentedtileProps.formatter. Layout and card must go in two separate writes (PnP or REST/SPFx) - Non-web protocol in
hrefdrops the whole element βms-word:in a formatter link removes the element with all children, per item, no error; open-in-app =openContextMenu .htmlin a library cannot be iframed β SharePoint serves library HTML as an attachment, so the iframe loads empty; host it elsewhere and allow that host in HTML Field Security- Seed idempotency must key on the item β A per-SET presence check re-inserts the whole block; SharePoint cannot enforce a unique set + value pair, and two app starts at the same moment still race β after inserting, re-read and delete your own copies that are not the oldest
- A Choice value the field does not know is stored anyway β No 400, no error to swallow: a drifted vocabulary never shows at runtime; machine-check the type-to-schema contract
- Version-gated provisioning drops elevated settings β A settings PATCH needs Manage Lists; the first member to open the app after an upgrade burns the one attempt and the version is stored anyway
- Item-level permission defaults on provisioned lists β ReadSecurity=2 looks perfect to an admin and empty to everyone else; WriteSecurity=2 breaks collaborative edits only
- View formatting lands on the wrong view β Site Pages has no
AllItems.aspxand the default view may be grouped; a column missing from the view isundefined, so the empty-check guard never hides it - Created/Modified on a document library β A plain MERGE reports 204 and SharePoint overwrites both with "now";
ValidateUpdateListItem+bNewDocumentUpdateis the only path that holds, it wants the web's locale format (the error message tells you which), reads it in the web's regional time zone rather than the browser's, truncates to the minute even when you send seconds, and dropsAuthorwithout a word unless all four author/date fields arrive together - Column internal name comes from DisplayName β
createfieldasxmlon a list ignoresName/StaticNameunless you passOptions: 8(AddFieldInternalNameHint); otherwise the internal name is escaped from the label β create with ASCII and rename, or use the hint to keep a source name - Indexing built-in fields (Title, Created) β 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
- Reader-written counters belong in their own list β Views and ratings are written by the reader, so on a
WriteSecurity: 4content list every click is a swallowed 403 while the UI says thanks - Item IDs are never reused β unless the list is recreated β 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
length()is for arrays, not strings β String length viaindexOf(str + '^', '^'), or the expression collapses to emptyMajorVersionLimit: 0means unlimited, not none β One integer covering "no limit", "keep n" and "versioning is off"; the library your report calls tidiest is the one eating the quota- A version is a full copy, not a delta β 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
- View formatting drops CSS gradients β
backgroundis not on the style allowlist and a gradient inbackground-imagedid not survive either; a white initial on a gradient avatar turns invisible, so use a solidbackground-color - A formatter renders empty in the tab you are iterating in β 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
- Client-written identity vs. the server-stamped Author β Append-only lists let any contributor add a row with someone else's name in a text column;
Authoris stamped by SharePoint. Compare the two β but againstAuthor/Name(the sign-in claim) as well, or every user whose e-mail differs from their UPN looks like a forger - Group header date = reader's display string β
@group.fieldDataof a Date field is text in the reader's locale; reformat only when the shape matches - Multi-line text in a document library stops at 255 characters β 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
- List items with authors, dates and history β Copy and Move does not do lists, but REST does the job item by item β
AddValidateUpdateItemUsingPathtakes Author, Editor, Created and Modified at creation (one call, one version), version history can be replayed withValidateUpdateListItem(bNewDocumentUpdate:false), approval goes in the same call, attachments add a version, two field formats are dropped without any error, and a user who may only add items cannot set the four fields β SharePoint silently keeps the real author and time
- permissions/
- WriteSecurity 4 ignores Contribute β 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
- PermissionKind is 1-based β The flag for kind k is
1 << (k - 1), so1 << kinvents a security finding: Contribute appears to holdManageLists(it holdsViewFormPages). Calibrate any decoder against a known level first, and note the tell β a decode that yields a role SharePoint does not have - Breaking inheritance without copying keeps only you β
copyRoleAssignments=falseleaves 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 with that feature's limits switched off, so the hardening pass lowers security for the very account it locked out - Limited Access cannot be granted β
RoleTypeKind: 1is derived, not assignable: SharePoint maintains it so people can traverse to a child with unique permissions, and an explicitaddroleassignmentreturns 200 while doing nothing. Strip it from EVERY assignment when replicating a permission shape (not only where it stands alone), detect it by number rather than its localised name, and guard the write side too β a verification pass that keeps reporting non-problems teaches the reader to skim past the real one HasUniqueRoleAssignmentsproves the break, not the hardening β The flag istrueafter the break alone, so verifying on it reports "hardened" over a list whose trim never ran; with the safecopy=trueform that state is worse than inheritance. Readroleassignmentsand measureBasePermissionsbits β role names are localised andRoleTypeKindmisses a customer's own level- A status code measures the order of checks, not permissions β A probe that writes with a deliberately stale
IF-MATCHand 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 the account has noEditListItems. AskEffectiveBasePermissions, run under an ordinary account, and print the inputs beside the verdict - A one-sided permission check passes on an empty ACL β "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 silently drop a group out of the policy
- Approving Graph permissions for SPFx needs a Global Administrator β API access requests for Microsoft Graph need Global Administrator (Application Administrator only covers third-party APIs), and admin consent for Graph application permissions such as
Sites.Selectedis explicitly excluded from Application/Cloud Application Administrator; put the role, with the citation, into the customer's preflight - The tenant root site is not necessarily readable by everyone β 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 withgetusereffectivepermissions - A count over a ReadSecurity 2 list is only the reader's share β Item-level read security filters rows silently, so a client-side count or ranking is the reader's own rows; show it only with
ReadSecurity = 1or Manage Lists in the reader's mask, and don't also demand Override List Behaviors β the Edit role (0x3C431AEF) lacks it - Counting a site's audience from its associated groups silently misses nested groups β 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 group as 'audience unknown'
- Upload-only drop box for sensitive files β Giving someone write access to a protected library folder also lets them read every file in it; a list with
ReadSecurity = 2, Contribute for the senders and Edit for the reviewers lets people send files they alone can see β the direct attachment URL of someone else's item is denied in SharePoint Online (measured) - Add-only permission sync leaks on owner change β Idempotent grant-only re-sync is safe at item creation but not when an item moves to another customer or team β the old group stays on the item; remove by principal Id, keep a protected set, and verify with a second roleassignments read
- Effective permissions ignore security groups β Asked on behalf of another user,
GetUserEffectivePermissionscounts Microsoft 365 group membership but not Entra security group membership β directly assigned or nested in a SharePoint group, still after 40 s β so it reports 'no access' for people who do have it once they sign in. Ask with the group's claim instead (that works), union the roles reached through the user's transitive security groups, treat a mask without any content bit (Limited Access, even with extra bits) as no access, and drop NoScript-stripped bits (AddAndCustomizePages) before mapping a mask to a permission level - Contribute on the web can edit and delete site pages β Modern pages are items in the Site Pages library, which inherits from the web, so a group you give Contribute on the web "so it can write list items" can also edit and delete every page; withholding Manage Lists does not help β break inheritance on Site Pages and give app roles Read there
- Guests cannot join Teams shared channels β Shared channels admit external people only through B2B direct connect between two Entra tenants; a guest account cannot be added, so a partner without their own Microsoft 365 needs a different design
- Item edit rights cover every field β 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
- FirstUniqueAncestorSecurableObject has no type in nometadata β The endpoint returns a web, a list or a list item depending on where inheritance breaks, and with
odata=nometadatanothing names the type; classify by properties (FileSystemObjectType= item,BaseTemplate= list, otherwise web) and treat an unknown shape as restricted - Narrowing to Read leaves the principal with nothing β 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 principal ends with no access and the routine reports unreconciled forever
- Page-only reader is denied at the site root β When a principal has Read on a single page in Site Pages and only Limited Access on the site, opening the site URL ends on AccessDenied.aspx even though the page itself opens fine β so a link that falls back to the site root breaks exactly for the narrow audience it was meant for
- Restoring inheritance is a one-way door β
resetroleinheritancethrows 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 withcopyRoleAssignments=truegives you the parent's ACL, not the old one, so the undo has to reconcile (add missing, remove extra) against the snapshot β keyed by principal id and role definition NAME, skipping Limited Access - Editing others' items needs Override List Behaviors β On a list with item-level write security, the Edit role (Manage Lists) still gets 403 when it updates an item someone else created; only a role with Override List Behaviors (
CancelCheckout, Low 0x100) gets through, so moving a group from Full Control to Edit silently takes that ability away
- spfx/
- A zip-bomb guard on a private JSZip field fails open β
entry._data.uncompressedSizeis an implementation detail, andundefined β allowturns the next library upgrade into a silent loss of protection; read the declared sizes from the ZIP central directory instead - Hand-built .docx / .pptx: the parts Office demands β Header images are referenced from the header's own rels, Content_Types must know the image extension, namespaces are per part, and a .pptx without theme + master + layout is invalid; split parts from zipping so a strict XML parser can check them in tests
- Graph grants are tenant-wide β
webApiPermissionRequestsis a request, not a grant; an approval lands on one tenant-wide principal, so a scope another solution had approved already works in yours (and may be missing at the customer) - No "grant it for me" button in a web part β A delegated call gets the intersection of the user's rights and the app's grants, so the admin at the keyboard adds nothing; writing the grant would take at least
DelegatedPermissionGrant.ReadWrite.Allapproved for the principal every SPFx solution shares, and approval runs on the admin site. Give the admin the path instead: API access first, a script second, and what the approval does not guarantee - Protocol-relative URL defeats a host allowlist β 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 test outside the release command guards nothing β
heft buildruns no Jest and the two prebuild scripts drift, so a rule can look covered while never executing on the release path - Polling the manifest proves nothing β A static host publishes the small manifest before the large entry bundle, so the component loads nothing and logs nothing; and an A/B against the previously deployed version compares deploy age, not code (+ a 503 on one file next to a 200 on its neighbour is the same deploy window)
Promise.alldiscards the soft reads it was waiting for β 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- An unparseable JSON column is not an empty column β
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 explicit action - The tenant's CSP is inherited by
srcdocframes β Modern pages sendscript-srcwithout'unsafe-inline', and a document you build yourself (srcdoc,blob:,data:) inherits it, so every inline script in the embedded HTML is dropped; an external file from an allow-listed source (including'self') still runs - Custom API permission request is "not valid" β API access resolves
resourceto a service principal, so the app registration must exist before the.sppkgis uploaded - A global kill-switch must not block its own fix β The lock defends itself against being lifted; infrastructure operations (version pin, re-check, config, diagnostics) belong outside it
- The ES2015
libtrap β TS2550 onpadStart& friends, and the safe equivalents - Dropping a folder does nothing β
dataTransfer.filescannot see a folder,itemsis neutered after the firstawait, andreadEntries()returns the contents in batches of 100 - Opening a pre-filled e-mail in the desktop client β No API tells you which mail client is default;
mailto:reaches it but carries no attachment, and a.emlneedsX-Unsent: 1or it opens read-only window.open(β¦, 'noopener')returnsnulleven when the tab opened β Code that readsnullas "blocked" runs its fallback every time: the e-mail opens twice (Outlook on the web andmailto:), the print window stays empty; open without the feature and setw.opener = null- Instrumenting the Graph client fails silently β Reassigning
client.apithrows and your fail-safe catch hides it; useObject.createand assert it attached - SPA router hijacks anchor clicks β
<a href>navigates before ReactonClickruns; use buttons for in-app actions stopPropagationinonClickdoes not stop the keyboard β A clickable row withonKeyDownalso receives the Enter/Space of its inner buttons and checkboxes, so their action is cancelled (or runs twice) and the detail opens; react only whene.target === e.currentTarget- Third-party CSS breaks webpack β
url(images/...)without./kills the build; inject a<link>instead - Minified React errors cheatsheet β #310/#300/#321/#31/#185 decoded for SPFx debugging
- People search endpoints that work β SP Search People source +
ensureuser; why the obvious endpoints fail - Fixed dropdowns in transformed panels β The CSS transform containing-block trap; portal to
document.body SP.WebProxyis add-in-only β No SharePoint-native CORS proxy for SPFx; the 403 hides inside an HTTP 200- Teams personal app needs global deploy β
skipFeatureDeployment: true+ "all sites"; theteams/icon-folder convention - Teams mobile webview renders desktop width β ~980px layout; fix the viewport meta in Teams first
- Shared component measures the viewport, not its container β
@medianever sees the narrow panel you were reused in; measure the element withResizeObserverand switch every child together - CDN-hosted bundle still needs a new
.sppkgβincludeClientSideAssets: falsemoves assets, not versioning; the manifest pins a content-hashed filename, so old packages never request the new bundle - CDN pruning deleted the extension bundle the .sppkg still referenced β With
includeClientSideAssets: falsethe catalog pins hashed names for customizers and command sets too; a "keep N newest" prune evicts the referenced hash and the extension 404s for days β protect extension bundles by name pattern or give them stable names - List pages run a stale extension bundle from Cache Storage β Stable (unhashed) bundle URL = list and library pages can run a days-old copy from the Lists app's own Cache Storage (
Lists-odsp-web-prod_*), whateverCache-Controlsays; keep the stable file a tiny loader and load real code from a versioned URL npm audit --omit=devoverstates shipped risk β SPFx keeps its Node build toolchain independencies, so 229 βproductionβ findings can hide a single real one; grep the published bundle and always include a control sample- Office file extraction needs a decompressed-size cap β An upload-size limit only bounds the compressed archive; a crafted
!refstill OOMs the tab - A Promise over img.onload can hang forever β onload/onerror are not guaranteed to fire; try/catch guards rejection, not a Promise that never settles
- Shared package's dynamic import ships inlined β A linked TS package built with module:commonjs turns import() into require(), so webpack can't lazy-chunk the lib into a separate file; set the package's module:esnext
- cultureInfo returns the web or the user language β The same page loads English once and Czech the next time; cache the resolved language per web instead
- Centered flex clips on mobile β Centering + overflow cuts content above the scroll; flex "springs"
- JSX attributes and smart quotes β Typographic quotes in attributes = TS1003; wrap as
{'β¦'} - A conditional spread hides a phantom list column β
...(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 shared component's var() fallback chain is only as good as its last link β Shared UI package falls through to
sans-serifin apps that never publish CSS custom properties; runtime-only, no build catches it - Read-only mode must cover Graph too β An SPFx app writes through two clients; wrapping
SPHttpClientleaves mail, calendar and Planner wide open - Portaled overlays miss your CSS reset β Portal to
bodyescapes the scoped reset;content-boxfields overflow and grow a scrollbar - A theme override on your app root can't reach tokens declared on
:rootβ The accent inherits, everything derived from it doesn't; substitution happens where the token is declared, portals miss the override entirely, and a colour written as a literal hex is unreachable either way - An element selector outranks your button class β
.app-root a { color: inherit }beats.btn--accent, so link-styled buttons get an unreadable label β but only on a tenant whose accent needs white text behavior: 'smooth'is silently ignored inside a portaled overlay β Jump-to-section does nothing in a modal/reader; drop the flag and setscrollTopdirectly- Previewing library files in your own app β PDF embeds directly, Office needs WOPI by
{UniqueId}, HTML is served as an attachment and never renders fromsrc; SVG and HTML must never be framed from your own origin (uploader script runs in the viewer session) and OneDrive refuses framing with no way to detect it;?web=1on a PDF ends on AccessDenied for users with access only to the file or its folder - Auto-save must wait for async inputs β Computed-from-async auto-save fires on mount with empty inputs and persists garbage; gate on a loaded flag
- Mermaid clips node text with a web font β Measure-before-load (FOUT) sizes boxes for the fallback font; use a system font stack
- rules-of-hooks false-positive from a JSX
&&chain β A complex conditional in your render blames the wrong hook; extract it to aconst. Second trigger: sheer component size β there extracting conditions doesn't help, splitting the component does. Third trigger: the recommendedconstextraction ITSELF β only block-by-block bisection from a clean HEAD finds the guilty line - A cached dynamic
import()caches the rejection too β One chunk-load blip poisons the module-level promise for the whole session; reset it to null in.catchso the next call retries jest.mock()doesn't hoist under Heft β Tests run over pre-compiledlib-commonjswithout Babel, so a mock written below the imports lands afterrequire; write it above the imports or usemoduleNameMapper(and why only some sp-http suites die on@msinternal/ecs-flight)- Application Customizer runs again in dialog iframes β A floating button rendered twice, the copy pinned to the dialog's corner; refuse nested browsing contexts before anything else (+ un-render path and why a body MutationObserver needs a definitive βnoβ)
speechSynthesis.cancel()doesn't stop chunked reading β The utterance chain re-queues itself from onend; epoch counter bumped BEFORE cancel(), and no onEnd from a superseded chain- Application Customizer's floating UI disappears on SPA navigation β The button vanishes as you click around the site;
visibilitychangenever fires on in-page nav, so re-render onnavigatedEvent(+ aMutationObserverbackstop that stops re-reading configuration after a definitive 404/403 and never reacts to its own writes) - Per-user data vanishes for admin accounts β 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 - Deep-link param appended to a page URL that already has one β The address an admin pastes already carries your routing param, so appending makes it appear twice and
URLSearchParams.getreads the OLD value; replace the key, never append - serverRequestPath is not the page URL β It is the last SERVER REQUEST path, so a modern list view can hand you
β¦/RenderListDataAsStream; validate the shape before storing it as your app page - Parser entry and collector must share one shape β A branch that accepts
1)but collects only1.spins forever on one line until out-of-memory; one shared regex, a no-progress contract and an outer backstop, each sufficient alone - Sharing text via URL hits length limits β "Send to Teams" (
/share?msgText) dies withAADSTS90015on long answers andmailtosilently won't open; cap the URL payload and put the full text on the clipboard - Provisioning step gated on success runs forever β A startup step needing ManageLists writes no marker for ordinary members, so the whole batch replays on every page load; the marker needs two states
- Setup marker written although a write failed β The success flag measured the id lookups, not the
addroleassignmentPOST, so a 403 froze a site with groups that carry no permission level at all - Command set button never appears β Two independent causes: uploading a new .sppkg registers the extension on no site by itself (register it at runtime, or deploy it tenant-wide with the all-sites tick), and
raiseOnChange()re-readscommand.visiblewithout re-runningonListViewUpdated - Injecting your own column into the modern list grid β An appended cell lands in the wrong row and shifts the table; rows are
display: contents, so the cells are the grid items and every one carries explicitgrid-column/grid-row - Reading the modern list selection from the DOM β Only a
ListViewCommandSetgetsselectedRows; from the DOM take names only (the name is a button, not a link), resolve paths with one folder-scoped call, and take the real count fromclearSelectionCommandbecause the view is virtualized - A command set's commands are fixed in its manifest β
title,iconImageUrl,visibleanddisabledchange at runtime, new commands need a new.sppkg; ship one entry that opens your own menu, or reserve hidden slots (and keep their titles honest) - A library component loaded from your own manifest fetches its lazy chunks from that manifest's folder β
SPComponentLoader.loadComponent+ a relocated manifest: without thechunk.*files next to the bundle everyimport()404s on first use, which an open-the-app smoke test never exercises - An SPFx chunk's file name is not its content β SPFx builds with
realContentHash: false, so a byte-identical chunk can get a new name (measured) and nothing rules out the reverse; deduplicate by bytes or git blob and never overwrite a published file - An overwritten script with a stable name keeps serving the old code β
max-age=600on GitHub Pages, a memory cache shared by same-site tabs and SharePoint's service worker; keep stable files thin, test withcache: 'reload'and every tab closed, and don't readtransferSizeof a cross-origin script - The "without a token" counter measures every client β You add Entra ID token validation to an API your SPFx solutions call, ship it in soft mode and wait for "calls without a token" to reach zero β it never does, because a shared npm package, a second solution, a diagnostic "check model" call and a secondary host such as a list-view command set call the same API without the header. Inventory callers by endpoint, not by app; create the token source once per app root and pass it as a value (a module singleton does not cross bundle boundaries); guard each client with a source-level test
heft --cleanleaves stale tests inlib-commonjsβ In SPFx projects built with Heft, Jest runs the compiled tests inlib-commonjs, but the rig's clean step deletesdist,lib,release, generated.scss.ts/.resx.tsfiles and parts oftemp(andjest-outputfor the test phase) β neverlib-commonjs. Delete, move or rename a test insrcand its old compiled copy stays there and keeps running, even inheft test --clean --production. Deletelib-commonjsafter moving or deleting files, or remove orphaned compiled files in a prebuild step- Print layer must be a direct child of
<body>β One way to print a report from an SPFx web part is to hide everything in print (body > * { display: none }, so the SharePoint suite bar, navigation and footer stay out) and show only a dedicated print layer. If that layer is rendered inside the web part, it is a descendant of a hidden element and the printout is blank. Render the print layer with a React portal straight intodocument.body - Teams user ID belongs to the host site β
legacyPageContext.userIdin 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 - Docking a side panel on the newer SharePoint layout β A Copilot-style side panel narrows the SharePoint page by shrinking
.sp-App-bodyContainer. On the newer page chrome (left app bar with Discover/Publish/Build) that container does not exist, so the docking code silently falls back to a floating panel with gaps around it. Dock against#spPageChromeAppDivinstead, and shrink it withmargin-rightβ it is a flex item, sowidthhas no effect. - M365 roadmap API has no CORS β
releasecommunications/api/v1/m365answers 200 withoutAccess-Control-Allow-Origin; proxy it server-side - pdf.js refuses two renders into one canvas β A React effect that re-renders a PDF page on every resize starts a second render() before the first finishes; cancel the running task in cleanup and chain renders so they never overlap
- Extension bundle imports a module the manifest lacks β Replacing an extension's stable-named bundle on the CDN without a new .sppkg works only while its
define([...])lists modules already in the installed manifest'sscriptResourcesβ one new value import and the extension silently never starts - DecompressionStream and trailing bytes in PDF streams β Inflating a PDF FlateDecode stream in the browser fails completely because a line break sits between the data and
endstream; trim trailing CR/LF and read chunk by chunk, keeping what arrived before the error - Audio in SPFx/Teams must be optional β SPO's CSP has no media-src today, so MP3 from your CDN plays; start playback from a click and fall back to silent timing on NotAllowedError, 404 or mute
- Power Automate admin API is unreachable from SPFx β SharePoint's token for Microsoft Flow Service carries only Microsoft's preauthorized scopes, so an approved
Usergrant never reaches the token and the tenant-wide flows list stays 401 window.scrollTodoes nothing in an SPFx web part on a modern page β The page scrolls inside a canvas element, not the window; switching views in a single-page web part leaves the user mid-page. Find the real scrolling ancestor and reset that- Shared style tag β the first web part wins β A library that injects
<style id=β¦>only if the id is missing lets whichever bundle renders first decide the CSS for every web part on the page, so a fixed rule in a newer bundle can silently lose to an older copy that rendered first - A textarea jumps after you change its text from code and restore the selection β focus() scrolls the page to the element and, in Chromium, setSelectionRange scrolls the textarea to the end of the selection; restore the selection with focus({ preventScroll }) and put scrollTop back
- A trailing-whitespace regex freezes the page when it parses AI output β /\s+$/ and lazy (.+?)\s*$ are quadratic on a line with a long run of spaces that does not end the line; text from an AI model or a foreign document is untrusted input, so trim with trimEnd() and anchor captures on a non-space character
- SPFx adds Subresource Integrity to a script when the manifest path is { path, integrity } β A component manifest you pass to SPComponentLoader.loadComponent can carry an SRI hash per script resource as path { path, integrity }; SharePoint then emits script integrity crossorigin=anonymous and the browser refuses tampered bytes β pin the bootstrap the same way from the .sppkg manifest (immutable CDN file), because includeClientSideAssets puts it in ClientSideAssets that guests cannot read; plus three traps that make console tests lie (id+version cache, requirejs path cache, CRLF in the git working copy); on list and library views SharePoint applies none of it, so a ListView Command Set or Application Customizer there runs unverified by the browser
- Load an SPFx bundle's lazy chunk from the browser console β no clicking through the UI β Push a fake chunk with a runtime callback into the bundle's
webpackJsonp_<id>array to get its real__webpack_require__, thenreq.e(chunkId)loads the chunk exactly as the app would β for checking 404s, SRIintegrityor CSP on features you would rather not trigger - Manifest SRI does not cover scripts your SPFx bundle loads itself (pdf.js, tesseract.js, workers) β Pinning the loader in the .sppkg and giving every manifest path and webpack chunk an integrity hash still leaves a hole β third-party libraries your own code injects with a script tag, fetches for a worker or that a worker pulls in with importScripts run unverified; keep their SRI hashes in the already verified bundle, set the integrity attribute (not the property), fetch worker code with integrity into a blob, and for tesseract.js 5 concatenate core and worker into one blob so importScripts is skipped
- Two "Uncaught (in promise)" from sp-pages-assembly on a modern page are SharePoint's own, not your web part β On SharePoint Online modern pages the console can show two "Uncaught (in promise)" exceptions with reason undefined and a stack only inside sp-pages-assembly, on every load and on SPA navigation β they also appear on a page with no custom code at all, so prove it with an A/B against such a page before you debug your own loader or web part; a page whose application customizer hides the SharePoint chrome may not show them, which makes "only some of my apps have it" look like a bug in those apps
- SPFx calling an intranet API hits Local Network Access β A
*.sharepoint.compage fetching a host that resolves to a private IP is a local network request β Chrome (from 142) and Edge prompt every user and a refusal is a bareFailed to fetch; pre-allow the SharePoint origin by policy and still plan for CORS, trusted HTTPS, VPN-only reach and per-user tokens β or let a connector inside the network write to a list throughSites.Selected - Hosted workbench restores web parts on another site β The hosted workbench keeps its canvas in sessionStorage per tenant origin, not per site; open another site's workbench in the same tab and your web part boots there β with that site's pageContext, so first-run provisioning creates lists on the wrong site. Clear
sp_webpart_workbench_statebefore switching sites.
- A zip-bomb guard on a private JSZip field fails open β
- app-catalog/
- Three
.sppkgpackaging pitfalls β ASCII-only solution name, icon exactly 96Γ96, Publisher column is AppSource-only
- Three
- graph/
/me/sendMail: From is always the signed-in user β DelegatedMail.Sendcan't impersonate; configure Reply-To instead- A
mailto:fallback reported as sent βsendMailfails on accounts without a mailbox and the fallback only hands a draft to a mail client; model sent / handed off / failed, never a boolean - "0 sent" hides whether anyone was asked β The recipient side of the same trap: a denied group read becomes an empty audience, an empty audience becomes a benign
false, and an approval queue notifies nobody - A newsletter looped over
/me/sendMailis not bulk e-mail β Per-mailbox and tenant limits, bulk mail through Microsoft 365 "isn't a supported use", JSONsendMailcan't setList-Unsubscribe, ACS Email retires 2028; keep Graph for personal batches, send newsletters through an ESP via a server-side connector - A pasted screenshot is too big for
/me/sendMailβ Inline base64 attachments share a ~4 MB request budget; shrink bitmaps in a canvas, cap the total, and remembermailto:fallbacks carry no attachments at all - ApplicationAccessPolicy rejects a shared mailbox β "not a security principal"; scope app-only
Mail.Sendto a mail-enabled security group /me/todoneeds an Exchange mailbox β 404 "Item not found" on admin/cloud-only accounts is not a 403; Planner works without a mailbox- A mail-permission probe that can't tell "no mailbox" from "no consent" lies to admins β
/me/messages404MailboxNotEnabled*on mailbox-less accounts β missing consent; three verdicts, and never cache a negative probe result - Purview Audit Query API is async β Hour-long queries; attach to the last succeeded, create in background
dont have any permissionsrejects the user, not the app β Consent vs. Exchange audit role; Global Admin usually inherits it, so compare accounts- A Teams channel exists immediately β its folder does not β
POST /channelsreturns 201 while SharePoint provisions the folder minutes later; never hand-make the folder, report the pending state and let an idempotent rerun finish it - Office files: property demotion changes the hash β Metadata PATCH and copy jobs rewrite Office, .msg and .htm file bytes; key change detection on
lastModifiedBy, never compare sizes exactly - Usage reports in the browser β use beta JSON β v1.0 302 to CSV dies on CORS; beta
?$format=application/jsonreturns 200 from Graph; mind roles and concealed names - Tenant-wide enumeration is app-only β
getAllSitesrejects delegated tokens; check the Permissions table first PATCH /me: directory vs profile fields β Mixed field groups fail whole; two PATCHes, profile best-effort- az CLI can't grant Sites.Selected β
sites/{id}/permissionsneedsSites.FullControl.All; the CLI client can't request it (AADSTS65002), use the Graph PowerShell SDK MSGraphClientcalls bypass DevTools Network β SPFx Graph traffic invisible in Network; diagnose viaperformanceentries,currentuser, DOM, user's response- preAuthorizedApplications needs an existing scope β Graph checks
delegatedPermissionIdsagainst stored scopes; scope and its pre-authorization need two separatePATCHcalls - Re-read fresh before bulk-removing members β The roster you showed is a preview; strict fresh re-read before a
removeMemberloop, read-error aborts, vanished member = idempotent skip perUserMfaStatesays "disabled" under Conditional Access β The legacy per-user switch stays off while MFA is fully enforced; report onuserRegistrationDetailsinstead β which omits blocked accounts and needs an undocumented Entra ID P1/P2 tenant- Someone else's group membership without
User.Read.Allβ/me/checkMemberGroupsonly answers for you; ask the GROUP for its transitive members instead, and never turn "could not determine" into "not a member" - The Graph JS client sends
$filtervalues unencoded β.filter(),.query()and.api('β¦?β¦')pass the value into the URL as-is (SDK 3.0.2, also under SPFxMSGraphClientV3):#cuts the query,&splits it and.api()even moves$topinto the filter; double apostrophes, thenencodeURIComponent - directoryObject collections reject
$selectβ and$topseparately β User fields on/members//transitiveMembers= 400; OData cast cures$selecteverywhere,$topstays per-endpoint (probe both) $filteron group members needs$count=truetoo β The header is only half the recipe; a permanently failing call hid behind a best-effort catch as a "temporary outage"- Purview audit
objectIdFilterscan return nothing β silently β Narrowed to one library: 0 records, no error; without the filter 1,000 records, 38 of them for that library β ask for a window, filter by object locally, A/B every server-side filter - A comma in a Graph permissions table can mean AND β Searching
chatMessageneedsChat.ReadandChannelMessage.Read.All; the 403 spells it out, the table does not (+webUrlvswebLinkfrom the same endpoint) - E-mail HTML built like a web page falls apart in Outlook β Classic Outlook renders with Word: no flexbox, rounded corners or gradients, so white text on a gradient vanishes; tables, solid colours, literal values, a full document, and under ~102 KB for Gmail
- signInActivity without P1 reports zero inactive users β Selecting
signInActivityon/usersfails with 403 in tenants without Entra ID P1/P2; the common fallback (retry without the property) makes every user look "never signed in" or gets skipped, so inactive-user and unused-licence reports silently show 0 β store an explicit "not measured" state instead - To Do rejects
$select(400 ParseUri) β Since Oct 2026/me/todo/listsand/tasksreturn 400 for any$selectexceptid; drop$select, keep$top/$filter, and measure 400s call by call before trusting a docs-based theory - Graph client network failure =
statusCode: -1β msgraph client wraps a failedfetchasGraphErrorstatusCode -1, codeTypeErrorβ detect it, don't report "permission denied" - serviceAnnouncement/messages stops at one page β
messages?$top=100&$orderby=β¦returned 100 posts without@odata.nextLink;$top+$skipreturned all 441 - Group
members/$refneeds object ID, not UPN βPOST /groups/{id}/members/$refandDELETE /groups/{id}/members/{id}/$refaccept only the user's object ID (GUID); a UPN from a people picker returns 400 Invalid object identifier β resolve it withGET /users/{upn}?$select=idfirst - Dynamic
memberOfrules freeze after 3 Nov 2026 β Entra ends thememberOfpreview operator in dynamic membership rules on 3 Nov 2026 (MC1448379): affected groups, dynamic admin units and auto-assignment policies keep their last membership and never update again. Offboarding tools that skip dynamic groups leave departed users inside them. The notice covers only the rule operator, not the Graph/memberOfendpoints. - Archived site returns 423 on every read β
archivalDetailsis unreadable once archived;POST /sites/{host},{siteCollectionId}/unarchivestill works - isPrivileged is beta-only β Read
isPrivilegedlive from betaroleDefinitionsβ v1.0 omits it and 400s on$select=isPrivileged; Exchange/SharePoint/Teams admins are not privileged - Channel messages need a Teams license on the reader β Delegated
GET /teams/{id}/channels/{id}/messagesreturns 403 'Failed to get license information for the user' for an unlicensed account even with ChannelMessage.Read.All consented and team membership; a licensed member gets 403 UnknownError on a private channel it is not in;allChannelsstill lists channels of teams the admin is not a member of - Audit query filters silently by scope β With only AuditLogsQuery-SharePoint.Read.All, Exchange and Entra queries return 201 β succeeded β 0 records; an unfiltered query returns only SharePoint records
- Exchange Admin API β no CORS from SPFx β adminapi/beta/{tenant}/InvokeCommand fails with 'Failed to fetch' even with a fake token; read Exchange config with an admin-run script and import, or use Graph message trace and mailbox reports
- Disabled accounts include shared mailboxes β blocked members β former employees; tell shared mailboxes apart via the mailbox usage report's
recipientType(needs real names) ormailboxSettings.userPurpose(per user) - Teams message sent as the user doesn't notify the sender β Delegated
ChatMessage.Senddelivers (even to the self chat48:notes), but Teams never alerts the sender; verify with GET +webUrl, notify from the app - sendMail without a mailbox β 504 and four attempts β No
MailboxNotEnablederror, just a 504 after ~30 s; the SDK RetryHandler repeats the non-idempotent POST up to 3Γ, so the button hangs ~2 min and a late success can send duplicates; checkassignedPlansfor an enabledexchangeplan first - PUT /groups/{id}/team on a new group returns 404 β
PUT /groups/{id}/teamfails with 404 for minutes afterPOST /groups(replication); retry only 404, surface a real failure as a warning - Suite incidents (MOβ¦) slip past a per-service filter β Filtering
serviceAnnouncement/issuesbyservicedropped MO1550821 (12 Nov 2026, users unable to connect to multiple Microsoft 365 services) β its service wasMicrosoft 365 suite
- azure-functions/
- Windows zip deploy breaks the running app β Live
wwwrootfile locks β whole app 503; re-run the deploy, prevent withWEBSITE_RUN_FROM_PACKAGE=1 - Verifying CORS by header presence passes every origin β The header comes back for every origin (servers answer with a fallback, not an omission), so presence proves nothing; compare it to the
Originyou sent, keep at least one case that expects allowed, and point the probe at a nonexistent route to prove it can fail - A server-side origin check rejects your own smoke test β Once the allowlist is enforced in the handler it applies to every caller, so the deploy script's own verification and the runbook command both need the
Originheader; "this test bypasses CORS" stops being true and hands the customer a red error on a healthy deployment - Rate limit counts the capability probe β Corporate NAT makes per-IP per-company; metered probes silently hide the feature
- A pinned Azure OpenAI model+version is a time bomb β "Deprecating" blocks NEW deployments well before retirement; resolve the newest GA version at deploy time, and match the deployment name to the model family
- "No such host" for
<app>.azurewebsites.netβ New apps get a unique default hostname (<app>-<hash>.<region>-01); the bare name never resolves, and the deploy log's.scm.URL reveals the real host even when the app name is a masked secret - Measure a third-party API before you build on it β The best-known free API returned 404/502/timeout on 7 of 8 live calls while the test suite stayed green; probe latency and status on real data, then cache successes (never failures) and honour
Retry-Afterβ a Function App's single outbound IP shares one quota - Cache-Control on a dynamic endpoint is uninvalidatable β
max-ageon a live aggregate lets a shared cache hold the response keyed by URL; neither an app restart nor deleting the underlying data clears it, only expiry β serve dynamic datano-store, cache server-side, and have the consumer call with?_=Date.now() - A brand-new Azure subscription blocks your first Function App deployment β New subscriptions start with
Y1quota 0 and unregistered resource providers; the quota is per subscription and per region, so try a neighbouring region first βaz appservice list-locations --sku Y1lists regions you cannot deploy into, butaz deployment group validatewith a swappedlocationmeasures it for real β register providers up front, and turn the ARM error into a diagnosis (self-service quota requests can be auto-rejected, so don't promise minutes) - Azure OpenAI 429: measure prompt size, not frequency β 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 pair, and remember that TPM costs nothing on pay-per-token SKUs, so an undersized default breaks traffic without saving a cent
- Retry only fast failures β a gateway error after 230 s is a timeout β The 502/503/504 a client rightly retries for transient failures also comes back when a request ran out of time (Azure Load Balancer returns 502 after 230 s), and retrying that multiplies the wait (3 Γ ~230 s) and the bill (Azure OpenAI charges for processing even when the request ends in an error) β decide by how long the failed attempt took, and give the server its own deadline below the platform limit with a status clients do not retry
Retry-Afterdoes not survive CORS β A cross-origin response exposes only the headers named inAccess-Control-Expose-Headersplus a short safelist, so an API that sendsRetry-Afterand a client that reads it still meet atnullβ both ends look right and a grep finds nothing; verify from the browser (nevercurl) and guard the list with a test over the build, proven by sabotage- pdf-parse and pdfjs-dist cannot share a process β Loading pdfjs-dist breaks pdf-parse's bundled pdf.js 1.10 ("bad XRef entry"), but only for classic xref-table PDFs and from the second request on β one PDF library per process; plus: load code under test with the loader production uses (createRequire, not
await import) - New GitHub repos send an OIDC subject with immutable IDs β New GitHub repositories send the OIDC subject as
repo:owner@ownerId/repo@repoId:β¦, so an Azure federated credential written in the classicrepo:owner/repo:β¦form never matches and azure/login fails with AADSTS700213. Read the repository'ssub_claim_prefixfrom the OIDC REST endpoint and build the subject from it - Unanchored
.funcignorepatterns strip your dependencies β Azure/functions-action applies.funcignorewith gitignore semantics to every path in the package, sosrc/ortest/without a leading slash also empties those folders inside node_modules. A dependency whose entry file lives insrc/(debug, for one) fails to load, the worker registers no functions and the function routes return 404. Anchor root patterns with a leading slash - Fixed
WEBSITE_CONTENTSHARE+ slots = production back on the old share β On a function app with deployment slots,WEBSITE_CONTENTSHAREisn't slot-sticky: a swap moves it together with the code. A template that pins the production value (saytoLower(appName)) would point production back at its pre-swap share, which the staging slot now uses, the next time you deploy it. Leave the setting out of the template, as Microsoft recommends - Azure OpenAI deployment type decides where prompts are processed β A GlobalStandard deployment in an EU region may process prompts in any Azure region; only Data Zone (EU Data Boundary) or regional Standard keep processing in the zone or geography β write data-location claims from the SKU your script actually creates, and print it
- Public-mode flag opens every endpoint, not just the chat β One Function App serves a signed-in SPFx app and an anonymous website chatbot; the website instance switches off the token check, and every other HTTP function in the same app (usage stats, storage read/write, file parsing) quietly becomes anonymous too β gate each non-public endpoint on the mode flag and enforce it with a test that scans every app.http registration
- Windows zip deploy breaks the running app β Live
- search/
- Search ignores unknown managed properties β 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
- Site missing from the index looks like a permissions problem β Three queries tell you whether the site is in the index at all, before you debug a query that was never wrong
- Enumerate every site from the browser console β
contentclass:STS_Site OR STS_Webover 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 digest) - The crawl log DOES exist in SharePoint Online β Reachable only through CSOM DocumentCrawlLog via /_vti_bin/client.svc/ProcessQuery, gated by a separate Crawl Log Permissions grant that no admin role implies; returns ~52 columns incl. per-pass crawl timestamps, errors, NoIndex and delete state
- ViewsX properties sort only by
ViewsLifeTimeβ Windowed counts select but don't sort; re-rank client-side - Compare SharePoint paths decode-first β Normalize encoding + boundary-aware prefix, or matches never fire
- Don't trust the parsed-file-types table: SPO does index
.mdβ The official table omits Markdown, yet live SPO full-text indexes it; probe before architecting - Graph Search returns 0 hits β you passed the question as the
queryStringβ Translate questionβkeywords; "what's new" is a listing (*+ default date sort, custom sort = 400); per-entity KQL only as documented - An unparenthesized
ORsilently escapes your scope filter β KQL AND binds tighter than OR; wrapping the query in parens is necessary but NOT sufficient (text can break out of them), and an empty custom path list must not fall back to the whole tenant - Sensitivity labels in Search β property works, licensing gates it β
InformationProtectionLabelIdreturns a GUID only after AIP-enable + label + crawl; unlicensed tenants can't even create a label (InvalidLicenseException) NoCrawlon a library silently blinds your RAG β Excluded containers return 0 with HTTP 200 and no log, so Search-backed AI never sees them; provisioning that excludes data lists must not inherit that onto content libraries; reindex can't be forced over REST (SP.PropertyValueswon't MERGE); indexing is not a permission change β Search trims per user; andHiddenis a separate axis fromNoCrawl, so hiding a library does not take it out of the index (measured before/after)- Duplicate trimming hides the file copies you search for β Search collapses identical content by default, so a copy-finder returns "no copies" exactly when perfect copies exist;
trimduplicates=falseon duplicate-hunting queries only - Search cannot see text inside images β and what that does to RAG β The crawler indexes the text layer only, so a value that exists only in an embedded flyer or a scan can never be found by keyword: a search-then-read pipeline is in a closed loop, the LLM re-ranker drops such documents (it scores text-layer snippets), and the only real fix is transcribing images into a crawled column
ParentLinkfrom Search points at a library view β Oftenβ¦/Forms/AllItems.aspx, so used as a path it empties aPath:filter and can send a save intoFormswhile aGetListprobe still passes; normalize on read and on write- RAG finds nothing β a reasoning model used the token cap for thinking β GPT-5/o-series count hidden reasoning in max_completion_tokens; a small cap on the query-generator call returns empty text (HTTP 200), and a raw-question fallback ANDs every word in KQL
- Inflected-language queries hit 0 β use
stem*, and zero hits is not a listing β KQL is implicit AND and matches exact word forms, so Czech inflected forms miss; add astem*variant in code, relax before giving up, and never serve a date-sorted inventory as sources for a specific question Size>=a Size<=breturns everything β same-property restrictions are OR-ed β KQL joins restrictions on the SAME managed property with OR when only a space separates them, so a size or date range written as two restrictions matches every item; write ranges with explicit AND or a..bSPSiteUrl:or("x")returns 0 β scope Search to a site list via postquery RefinementFilters β To restrict any Search query to an explicit list of site collections, POST to /_api/search/postquery with RefinementFilters SPSiteUrl:or(...) (tested with 3,000 URLs); a single value must use equals() because or() with one value returns zero rows; and LastModifiedTime of an STS_Site row says nothing about activity on the site- Search 500 / 429 without Retry-After are transient β retry the read β 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 backoff and show a truthful error with a Try again button
- powershell/
- PS 5.1
Get-Contentmangles UTF-8 β ANSI default double-encodes diacritics; non-ASCII in the pattern makes-replacematch nothing and exit 1. UseSystem.IO.File+ BOM-lessUTF8Encoding, checkgit statusafter a failed run - Smart quotes are string delimiters β
β/"parse like"; localized text in single-quoted here-strings 'Stop'+ a native stderr warning = terminating error β$ErrorActionPreference='Stop'escalates a benign stderr warning (exit 0) to a script-killer; wrap in'Continue', judge by$LASTEXITCODEGet-PnPList -Identityrejects server-relative URLs β Title/GUID/web-relative only;/sites/team/sharedfails as "List does not exist"- PS7
[ref]arguments break Office COM calls βSaveAs2([ref]$x)dies on "psobject to Object"; pass values directly - A syntax check under PS 7 proves nothing about 5.1 β The parser belongs to the engine hosting it; ternary/
??/&&pass in 7 and are parse errors in 5.1, and#Requiresnever warns you at authoring time Export-Csv -Encoding UTF8flips the BOM between 5.1 and 7 β Same parameter, different bytes; Excel reads encoding from the BOM, so the report is readable on one machine and mojibake on the next$hostis a constant you cannot assign β The obvious name for[uri].Hostis reserved and the error blames the value;$input/$argsaccept the assignment silently and break later- PS 5.1:
@(ConvertFrom-Json)keeps the array wrapped β@()around the pipeline doesn't enumerate:foreachover it runs ONCE with the whole array, and a later+nests the array soConvertTo-Jsonwrites{value:[β¦],Count:n}garbage; fix with@((ConvertFrom-Json β¦)) --query "length([?β¦])"from PowerShell killsaz.cmdβ On Windowsazis a batch file; a JMESPath query with()and no spaces reaches cmd unquoted and breaks its IF block β return plain names and count in PowerShell- File names in a generated
@" "@here-string run as code β Data that tenant users can name (file paths, titles) breaks every textual PowerShell literal β expandable here-strings run$(β¦),;splits CSV rows,'@ends a literal here-string; embed data as base64 JSON instead
- PS 5.1
- security/
- Sanitizer keeps script and style TEXT β An allowlist needs two sets: tags to keep, and tags to drop WITH their subtree; otherwise a pasted web page dumps its stylesheet into the article as visible text
- Stored XSS via list content β React doesn't block
javascript:hrefs; allowlistsafeHrefwith C0-strip at every sink - A formatter that returns raw input is a hole in your escaping β The unhappy branch returns the value verbatim;
about:blankinherits your origin - Field hiding is not a permission β 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 sibling column that still holds the number, and "not allowed to read" arriving as a legitimate 0 in every aggregate and downloaded file
- Fewer sites and sudden 403s mean a different account β A tenant-wide snippet run from a second browser window executes as whoever is signed in there; print
currentuserand the visible-site count before you blame the tenant - Effective permissions come as a bitmask β
ViewListItemsis bit 0,Low/Highare 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 - CSV export executes formulas β A member-written cell starting
= + - @(or TAB/CR) runs in Excel on the reader's machine; quoting doesn't disarm it, an apostrophe prefix does - URL sanitiser strips spaces β The C0 strip that blocks
java<TAB>script:also eats the plain space, so every link to/Shared Documents/β¦404s; strip to decide, return with%20 - An
image/*upload accepts SVG β SVG is a script: inert inside<img>, executed when the file's own URL is opened; allow-list raster MIME types - Breaking inheritance copies foreign Edit grants β 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, becausecopyRoleAssignments=falsekeeps only the caller - App-provisioned libraries inherit the web's write permissions β Any member can upload over REST and poison an AI grounded on that library;
WriteSecurity: 4does not fix it (Edit includes Manage Lists and bypasses item-level settings) β only unique permissions on the library do - A group created by code hides its own membership β
sitegroupsPOST defaults toOnlyAllowMembersViewMembership: 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 - An Entra group inside a SharePoint group is invisible to membership checks β SharePoint accepts an Entra group as a member of a site group, then never expands it:
currentuser/groupsis direct-only andsitegroups/{id}/usersreturns the group, not its people β resolve with GraphcheckMemberGroupsand keep "couldn't check" apart from "not a member" - Lock down a no-script site from code β On a modern site with custom scripts disabled,
NoCrawlon the WEB returns 403 via REST and CSOM even for a site collection admin, andSetUseAccessRequestDefaultAndUpdate(false)returns 200 without effect; both work through the site settings pages, whileMembersCanShareand the limited-access lockdown feature are writable via REST. Always re-read after writing a web setting.
- tooling/
- Driving an SPFx page from the console β
input.valuefrom the console leaves React state empty (use the native setter + input event) andwindow.confirmfreezes CDP automation; both fail silently as success - Measuring a page in a hidden tab β Hidden tabs never fire
requestAnimationFrameand clampsetTimeoutto ~1/s, so the run times out and looks like the very freeze you were chasing (and the code under test never ran either); CSS transitions never advance either, sogetBoundingClientRect()keeps returning the previous position - Diagnostics that cannot survive the crash β Frozen threads stop repainting,
sessionStoragedies with the tab and the next healthy run overwrites the crash report; persist synchronously and never gate recovery on a flag - Getting bulk data into a SharePoint page from the console β Serve the payload from
127.0.0.1with CORS instead of pasting it or uploading it; loopback is a trustworthy origin so HTTPS pages may fetch it β and when Chrome local network access blocks it ("Failed to fetch"), feed the files through a file input - Git Bash mangles backslashes for native exes β
[\\/]arrives as[/]; Windows-path regexes silently under-match - Contributors panel keeps a co-author you removed β Panel and API answer different questions (co-authors vs authors), so verify at the source; know the documented ~24 h window before you start waiting
- GitHub Pages certificate stuck β Domain added before DNS existed; remove & re-add restarts provisioning
- NUL byte makes grep treat a file as binary β One raw U+0000 and every grep-based sweep silently skips the file; write the escape, detect with
file - Compiled files next to sources fake your build check β Stale
.js/.d.tsinsrc/greps like live code whilemainpoints atlib/; askrequire.resolve, date-check withgit log, and keep the ignore rule narrow enough to spare hand-written.d.ts git rm -rleaves the directory, so tools report a phantom version β Untracked build leftovers keep a removed version folder alive on disk, so anything listing versions withreaddirSyncoffers a build the site no longer serves; read published content fromgit ls-treeand fail closed when git cannot be read- Wordβs PDF export silently substitutes your fonts β
ExportAsFixedFormatlays the text out in Calibri while Word still lists and embeds the font; print to PDF instead, install static (not variable) instances, and verify via thenametable inside the embeddedFontFile2 - A GitHub Pages CDN without
.nojekyllfreezes on its last good build β Branch builds run Jekyll unless.nojekyllexists; a growing bundle host ended in a bare "Page build failed." while the site kept serving the old build β read/pages/buildsbefore pushing again - Setting
process.env.TZinside Jest does not change the time zone β A test,setupFilesandsetupFilesAfterEnvwrite into the sandbox's copy ofprocess.env, so day-boundary tests pass only on machines already in the right zone; pin it inglobalSetup, assert the offset, and run once under a foreignTZ(not from Git Bash on Windows) - A retention window computed from git history deletes the file you overwrite in place β A stable loader keeps its add date and drifts out of "the last N releases"; protect by shape (no content hash), and remember the hard 1 GB Pages limit counts the published tree
- A component from a linked package throws "Invalid hook call" in Jest β Jest resolves a
file:/junction package by its real path, so the package'srequire('react')finds a second copy; mapreactandreact-domto the app's copy (the SPFx build is unaffected β React is a platform component) TextEncoder is not definedbefore your first test runs β jsdom has no Encoding API and a dependency that buildsnew TextEncoder()at module level dies on import, beforebeforeAll; import a polyfill module first or usesetupFiles, and make the dependency lazy- A bundle's
.LICENSE.txtnever reaches your CDN β webpack moves third-party license comments into<bundle>.js.LICENSE.txtand leaves only a pointer in the bundle; a publish step that copies*.js, or an allowlist.gitignore, ships the code without the texts MIT and BSD require. The file is build output: publish it next to its bundle, prune it with it, and make every build-vs-published check expect it process.exit()afterfetch()lies about the exit code on Windows β On Windows (seen on Node.js 24),process.exit()right after the built-infetch()sometimes aborts on a libuv assertion (UV_HANDLE_CLOSING) and ends with a crash code (3221226505, shown as 127 in Git Bash), so a check that has just printed OK reports a failure β intermittently. After the first network call, setprocess.exitCodeand let the process finish; callprocess.exit()only before it- A shell hook written without chmod runs on Windows and is skipped on Linux β Git ignores a hook without the executable bit, but Git for Windows does not check that bit and runs a hook that starts with
#!/bin/sh, so a fixture that writes.git/hooks/pre-commitwithwriteFileSyncblocks the commit locally and not on Linux CI;chmod 0o755after writing (harmless on Windows), and commit hook scripts withgit add --chmod=+x global.navigator = β¦for jsdom is ignored on Node 21+ β Node 21 added a globalnavigatorthat has a getter and no setter, so the classic hand-made jsdom setup line is silently ignored (strict mode throws a TypeError) and the test sees Node'suserAgent(e.g. "Node.js/24") and noonLine; define the property withObject.definePropertyand assert it took effect- Azure HD voice is generative β verify by transcription β DragonHD output varies per call and sometimes garbles a term; transcribe each MP3 with fast transcription and regenerate on a miss. Use
for a non-native language, playbackRate instead of the unsupported , and honor Retry-After on 429 - Root site swap refuses hub sites, and any site Publishing ever touched β
Invoke-SPOSiteSwaprejects a source or target that is a hub site or associated with one, and rejects a source where the Publishing feature was ever activated β not just currently; unregistering a hub leaves children appearing associated for up to an hour, so remove the associations explicitly before the swap, and never activate Publishing on the site you intend to promote - Jest's 5 s default timeout fails render tests under load β and the next test with them β The first jsdom + React test of a suite pays for the cold module load; on a busy build machine it passes 5 s, Jest abandons it while its async render keeps running, and the following test fails with "button not found" or a doubled write. Set
testTimeoutfor the project, not per test - Console fetch hangs under the ODSP service worker β A SharePoint page is controlled by the
odspserviceworkerproxy.aspxservice worker; when it gets stuck, everyfetchfrom the page (even to another domain) never resolves while timers still run β a hard reload (Shift+F5) loads the page outside the worker and the script works - ECDSA: Node DER vs WebCrypto r||s β crypto.sign in Node returns a DER-encoded ECDSA signature (69β72 bytes); WebCrypto verify in an SPFx bundle expects IEEE P1363 (64 bytes for P-256) and just returns false. Sign with dsaEncoding ieee-p1363, check the length, and test both directions with a DER counterexample
- Moving a static CDN off GitHub Pages without breaking the apps that load from it β Upload git blobs, not a Windows working tree (CRLF breaks SRI); allow the new CA in CAA; missing files must stay 404, not 403; Azure static website has no CORS and Front Door ignores the query string by default; folders without a trailing slash; upload content before pointers
- Driving an SPFx page from the console β
- rest-api/
- π§ guides/ β end-to-end walkthroughs
- Calling SharePoint REST like a pro β The client landscape, headers that matter, safe writes, reading well, field/list creation quirks, and a ten-minute diagnosis routine
- Search queries that actually work β The one mandatory header, practical KQL, managed properties (
RefinableString*), paging/sorting, and the freshness/trimming traps - SharePoint REST vs Microsoft Graph β A decision table by capability, the SPFx auth difference, common wrong picks, and throttling budgets
- Choosing a knowledge format for RAG β Markdown vs DOCX vs site pages vs list items β extraction quality, token economics, and the author-here-publish-Markdown pattern
- Token cost of content formats (measured) β Real numbers: the same article as
.md/.docx/.pdf/a SharePoint page β why extraction, not format, drives the token bill; plus a CZ-vs-EN language tax and a reproducible harness - Reverse-engineering Plumsail Forms apps β 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
- Provisioning folder trees at scale (measured) β 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=falsesilently dropping the owners group - Renaming your SharePoint domain β Scheduling a tenant rename on a fresh tenant β checking the name is free, adding the matching
.onmicrosoft.comdomain the right way, the 24-hour scheduling floor (error -772), what-WhatIfand the status output look like, and what to freeze until it finishes
- π course/ β SharePoint Online β administrace od A do Z (CZ course materials)
- Course overview β 8 chapters (chapter 02 is covered inside 01), what you'll learn, lecturer
- 01 Β· PΕedstavenΓ SharePointu β Positioning, scenarios, editions, SP Online vs Server, governance, roadmap
- 03 Β· InformaΔnΓ architektura β Terminology, sites vs subsites, hub sites, site types, templates
- 04 Β· SprΓ‘va podnikovΓ©ho obsahu β Lists & libraries, content types, Content Type Hub, managed metadata, retention
- 05 Β· ΕΓzenΓ oprΓ‘vnΔnΓ β Object model, security scopes, SP vs Entra groups, Everyone claims, Check Permissions, external sharing
- 06 Β· VyhledΓ‘vΓ‘nΓ obsahu β Indexing/crawl, KQL query syntax table, Modern Search, disabling indexing
- 07 Β· ZΓ‘lohovΓ‘nΓ a obnova β Versioning and what it costs in storage, recycle bins (93 days), Microsoft 365 Backup, third-party tools
- 08 Β· SharePoint reporting β Admin Center reports, usage, Microsoft Purview auditing, alert policies
- 09 Β· PowerShell β SPO Management Shell + PnP PowerShell, admin/reporting scripts (storage, guests, duplicates, long URLs, provisioning)
- π€ talks/ β slide decks from public talks and conferences (CZ)
- ZΓ‘pisky z bojovΓ©ho pole β 20 let se SharePointem β 22 slides of war stories as symptom β cause β lesson: the myths that persist, where permissions are lost, security without varnish Β· WUG Days 2026, Brno
- SharePoint jako znalostnΓ platforma pro firemnΓ AI β 25 slides on what to do before buying Copilot licences; AI amplifies your content's current state rather than improving it Β· WUG Days 2026, Brno
- βοΈ snippets/ β small copy-paste fragments
- rest/
- Read all items from a large list β paging done right β Any list past a few thousand items:
$topcaps at 5,000,$skipis ignored β followodata.nextLink. Plus the error branch:breakreturns a partial list that looks complete - Find externally / anonymously shared content via Search β Oversharing / Copilot-readiness audit:
ViewableByExternalUsers:1surfaces files shared out, security-trimmed, one query - Check another user's effective permissions β Verify what a normal user can actually reach β from your own session, no test account; mind
Open=Lowbit 16 - Upload a generated image as a list item attachment β Canvas charts, placeholder banners, QR codes β
AttachmentFiles/addtakes a rawArrayBuffer; the item must exist first - Write to Azure Table Storage without the SDK (SharedKeyLite) β Telemetry/counters from an Azure Function without
@azure/data-tablesbloat β Table-flavored SharedKeyLite signing, first-=connection-string parsing, 409 = fine - Resolve a pasted library URL (web, library, folder) β
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); stripForms/β¦aspx, prefer the?id=folder, take the first segment under the web as the library, checkBaseTemplate101 β all measured from the browser
- Read all items from a large list β paging done right β Any list past a few thousand items:
- cli/
- SPO Management Shell one-liners β Quick admin answers β storage top 20, external sharing, deleted sites, lock state β no script file needed
- rest/
- π¦ templates/ β reusable artifacts to adapt, not rewrite
- coming soon β see planned categories
- π resources/ β a deliberately short list of sources we actually use
- Official documentation β SharePoint dev docs, Microsoft Graph, admin docs, M365 roadmap
- Tooling β PnP.PowerShell, CLI for Microsoft 365, Graph Explorer, SP Editor
- Community β PnP community, sp-dev-docs issues, List Formatting samples, SPFx samples, look book
- π Repo meta
- README β the front page
- CONTRIBUTING β content formats, sanitization rules, PR process
- LICENSE β MIT
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