404 Not Found
Situations in the vault where SharePoint, SPFx, Microsoft Graph or the Azure services around them answer with HTTP 404. Each one explains why it happens and how to fix it.
- "File is missing" and "I cannot read the file" collapse into the same null — and the empty state then liesgotchas / rest-apiA T | null helper folds 404, 403, 500 and network failure into one null, and the friendly empty state then lies to the one user who has overdue work; return {ok, status, data} and let only 404 (on every candidate path)…
- Microsoft To-Do (/me/todo) needs an Exchange mailbox — admin accounts 404gotchas / graph404 "Item not found" on admin/cloud-only accounts is not a 403; Planner works without a mailbox
- A # or % in a file or folder name — the classic …Url APIs cannot reach it, and the old writes quietly save %25gotchas / rest-apiThe classic …ServerRelativeUrl lookups, Files/add(url=), folders/add, moveto and copyto cannot reach it even encoded: 404/400, Exists: false for a folder that exists, and writes that answer 200 but save a literal %25…
- For license information please see … leads to a 404 — the publish step left the bundle's .LICENSE.txt behindgotchas / toolingwebpack moves third-party license comments into .js.LICENSE.txt and 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…
- A deleted site and a site you cannot see are two different answers — read the status, not okgotchas / rest-api_api/web on a non-existent site collection answers 404 (measured, no redirect), so if (!r.ok) return unknown makes a dead row in your own site inventory look like a site you merely lack permission on; keep r.status…
- A GitHub Pages CDN without .nojekyll freezes on its last good buildgotchas / toolingBranch builds run Jekyll unless .nojekyll exists; a growing bundle host ended in a bare "Page build failed." while the site kept serving the old build — read /pages/builds before pushing again
- A library component loaded from your own manifest fetches its lazy chunks from that manifest's foldergotchas / spfxSPComponentLoader.loadComponent + a relocated manifest: without the chunk.* files next to the bundle every import() 404s on first use, which an open-the-app smoke test never exercises
- A mail-permission probe that can't tell "no mailbox" from "no consent" lies to adminsgotchas / graph/me/messages 404 MailboxNotEnabled* on mailbox-less accounts ≠ missing consent; three verdicts, and never cache a negative probe result
- A mailto: fallback that reuses the success state tells the user their message was sent when it wasn'tgotchas / graphsendMail fails on accounts without a mailbox and the fallback only hands a draft to a mail client; model sent / handed off / failed, never a boolean
- Your Application Customizer's floating UI disappears when navigating the sitegotchas / spfxThe button vanishes as you click around the site; visibilitychange never fires on in-page nav, so re-render on navigatedEvent (+ a MutationObserver backstop that stops re-reading configuration after a definitive…
- CDN pruning deleted the extension bundle the deployed .sppkg still referencedgotchas / spfxWith includeClientSideAssets: false the 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…
- fields/getbyinternalnameortitle returns 400, not 404, for a missing fieldgotchas / rest-apiIt throws ArgumentException (HTTP 400), not 404; an existence-check that hard-fails on non-404 breaks the "field missing → create" path, and one that creates on any non-200 duplicates the column when throttled
- A folder created with folders/AddUsingPath in a generic list has no list item — breaking its permissions returns 404gotchas / rest-apiIn a custom list (BaseTemplate 100) web/folders/AddUsingPath creates a 'naked' folder: ListItemAllFields is odata.null (@odata.null under SPFx's OData v4) and breakroleinheritance is 404. Create list folders with…
- Get lists by URL, not by titlegotchas / rest-apigetbytitle() breaks the moment someone renames a list; resolve by URL (bonus: GetList normalises folder paths to their list)
- getfolderbyserverrelativeurl returns 200 for a folder that does not existgotchas / rest-apiIt answers HTTP 200 with {"Exists": false} (and ListItemAllFields 200 with {"odata.null": true}), so a check on response.ok skips creating the folder and the next call 404s inside breakroleinheritance; measure the…
- GetStorageEntity returns 200 for a missing key, not 404gotchas / rest-apiUnset tenant property answers 200 {"odata.null":true}; "unset" and "failed" are indistinguishable by status code, and writing needs Tenant Admin — same trap in …?$select=Exists, which returns 200 {"Exists":false}
- git rm -r leaves the directory behind, so a tool reading the filesystem reports a version nobody servesgotchas / toolingUntracked build leftovers keep a removed version folder alive on disk, so anything listing versions with readdirSync offers a build the site no longer serves; read published content from git ls-tree and fail closed…
- GitHub Pages HTTPS certificate never arrives when the domain was added before DNS existedgotchas / toolingDomain added before DNS existed; remove & re-add restarts provisioning
- The best-known public API may be the worst one — measure before you build on itgotchas / azure-functionsThe 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…
- A missing attachment returns 400, not 404 – an "is it there?" check by status code reads "not there" as a failuregotchas / rest-apiAttachmentFiles('name') on a missing attachment returns 400 ArgumentOutOfRangeException; check existence from the attachment list
- Looking up a field that does not exist returns 400, not 404gotchas / rest-apifields/GetByInternalNameOrTitle on a missing field answers 400; test existence with a $filter instead
- Moving a static CDN off GitHub Pages without breaking the apps that load from itgotchas / toolingUpload 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…
- A "public mode" flag that disables the token check opens every endpoint in the Function App, not just the chatgotchas / azure-functionsOne 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…
- The Purview Audit Query API is asynchronous — think in hours, not secondsgotchas / graphHour-long queries; attach to the last succeeded, create in background
- Creating a team right after creating its Microsoft 365 group returns 404 — retry only the 404, and don't log the failure as successgotchas / graphPUT /groups/{id}/team fails with 404 for minutes after POST /groups (replication); retry only 404, surface a real failure as a warning
- .funcignore pattern src/ also empties node_modules/*/src — the deployment succeeds, the functions don't loadgotchas / azure-functionsAzure/functions-action applies .funcignore with gitignore semantics to every path in the package, so src/ or test/ without a leading slash also empties those folders inside node_modules. A dependency whose entry file…
- Your URL sanitiser strips spaces — and every SharePoint file link 404sgotchas / securityThe C0 strip that blocks javascript: also eats the plain space, so every link to /Shared Documents/… 404s; strip to decide, return with %20