Skip to content

Request_ResourceNotFound: the Graph object does not exist

Short answer: Request_ResourceNotFound means Microsoft Graph could not find the object the URL names. It covers a deleted object, a GUID that was never used, a value that is not a GUID at all, and a reference property pointing at something gone. A 404 also tells you something useful: your app does have read permission on that collection.

Fix: Check the object identifier. A non-GUID value and an unused GUID both return this, so confirm you are passing an object ID or userPrincipalName and not a display name or an app ID. If the object was deleted in the last 30 days, look in /directory/deletedItems to confirm, then restore it or stop referencing it. Full steps

At a glance
Error codeRequest_ResourceNotFound
HTTP status404
In Microsoft's Graph error documentationNo (checked ). The wording on this page is what Graph returned in the lab.
CategoryGraph request shape
Attack signalLow: usually configuration or user error

Graph resolved the URL to a real collection, looked for the object the URL names, and did not find it. HTTP 404.

The message is a single sentence with the identifier you passed quoted inside it:

Resource ‘<id>’ does not exist or one of its queried reference-property objects are not present.

That “or one of its queried reference-property objects” clause is doing real work. The 404 can be about the object you asked for, or about something it points at — our reading is that a $expand on a manager who has since been deleted would produce the same code as a user who does not exist, though the lab did not test an $expand.

The only Microsoft page that defines this code is the retired Azure AD Graph catalogue, last updated in 2016 and carrying a retirement caution, where the description is “The resource identified by the URI does not exist. Revise the value and retry the request.” Microsoft’s current Graph error reference lists HTTP status codes only and contains no named-code table. What makes the retired page still worth citing: the message it records for this code is, word for word, the message Graph v1.0 returned in a lab tenant on 31 October 2026 — so the 2016 wording is current service behaviour, even though the page it is on is retired.

On 31 October 2026 an automated run sent these against a lab tenant with an app-only token:

Request HTTP Message
GET /users/<unused GUID> 404 Resource '<id>' does not exist or one of its queried reference-property objects are not present.
GET /users/not-a-guid 404 Resource 'not-a-guid' does not exist or one of its queried reference-property objects are not present.
GET /applications/<unused GUID> 404 same wording
GET /servicePrincipals/<unused GUID> 404 same wording
DELETE /users/<unused GUID> 404 same wording
PATCH /users/<real id> setting a property that does not exist 404 Resource '' does not exist or one of its queried reference-property objects are not present.

A value that is not a GUID returns 404, not 400

Section titled “A value that is not a GUID returns 404, not 400”

GET /users/not-a-guid is a malformed identifier, and a strict API would reject it as a bad request. Graph treats it as a lookup that found nothing.

This matters when you are handling errors in code. A 404 from Graph does not mean “the object was deleted” — it can equally mean your variable held a display name, an email alias that is not a userPrincipalName, or an empty string that collapsed the path. If you are logging a 404 as “user gone”, log the identifier you sent alongside it.

A 404 from a PATCH can be about the body, not the URL

Section titled “A 404 from a PATCH can be about the body, not the URL”

The last row above is the most misleading result in these runs. The URL named a user that existed; the body set a property that does not exist. Graph answered 404 with an empty resource name rather than 400 naming the property.

We cannot explain that from the response, and it is worth knowing precisely because the obvious reading is wrong: someone debugging it goes looking for a missing user. If a PATCH returns 404 and you have verified the identifier, check the property names in the body before anything else.

A 404 tells you your app can read that collection

Section titled “A 404 tells you your app can read that collection”

In the same run, the identical request shape against other collections returned something different. The run also read each collection itself, which is the control that makes the pattern readable:

Collection GET /<collection>?$top=1 GET /<collection>/<unused GUID>
users 200 404 Request_ResourceNotFound
applications 200 404 Request_ResourceNotFound
servicePrincipals 200 404 Request_ResourceNotFound
groups 403 Authorization_RequestDenied 403 Authorization_RequestDenied
devices 403 Authorization_RequestDenied 403 Authorization_RequestDenied
directoryRoles not read 403 Authorization_RequestDenied
directory/administrativeUnits not read 403 Authorization_RequestDenied
contacts not read 403 Authorization_RequestDenied

The run recorded the application permissions in its own token, from the token’s roles claim: Application.ReadWrite.OwnedBy and User.ReadWrite.All, and nothing else. The collection reads above are what establish which types were readable; the roles are context, and the run never varied them to test the causal link.

The correlation is exact across all five collections where both levels were tested: every one the app could read answered a GUID belonging to nothing with 404, and every one it could not read answered the same thing with 403. The three marked “not read” were only probed with a GUID.

So the status code distinguishes two cases that look alike: a 404 means your app can read that collection and the object genuinely is not there; a 403 means you cannot tell whether it exists. Our reading of the mechanism is that type-level permission is checked before the object is looked up; the run establishes the correlation rather than the ordering.

Useful when debugging: if you expected a 403 and got a 404, your permissions are fine for that collection and the problem is your identifier. And if you are hunting a permission problem, a 404 rules it out there.

Low on its own, and not something to alert on by itself — a 404 is the normal result of a stale cache, a race against a deletion, or a sync job working from an old export.

The pattern worth knowing about is the 403/404 split described above, because it turns Graph into a permission oracle. An app walking made-up GUIDs across /users, /groups, /applications, /servicePrincipals and /directoryRoles learns which collections it can read from the status codes alone, without reading a single object and without any successful data access to notice. For a compromised service principal that is a quiet first step.

A second, weaker pattern: a long run of 404s against sequential or enumerated identifiers on one collection is object enumeration rather than a broken integration, because a broken integration repeats the same wrong identifier rather than cycling through new ones.

Neither of these appears in Entra sign-in logs — a Graph call that fails writes no sign-in row. The token acquisition behind it does appear in AADServicePrincipalSignInLogs, and it succeeded, so it looks like any other sign-in. The calls land in MicrosoftGraphActivityLogs: KQL for the object-lookup pattern.

  1. Log the identifier you sent. The message quotes it back to you, and most of these resolve the moment you see what was actually in the URL. A display name, an appId where an object id was needed, or an empty variable are the three common ones.
  2. Use the right identifier for the collection. /users/ accepts the object id or the userPrincipalName. /applications/ accepts the object id, and /applications(appId='<appId>') accepts the application (client) ID — these are two different GUIDs on the same app and swapping them produces this error. /servicePrincipals/ likewise has /servicePrincipals(appId='<appId>').
  3. Check whether the object was deleted rather than never created. Directory objects go to a recycle bin for 30 days. GET /directory/deletedItems/microsoft.graph.user lists soft-deleted users; POST /directory/deletedItems/<id>/restore brings one back. After 30 days the object is permanently gone and the reference has to be removed from whatever still holds it.
  4. If the object clearly exists, suspect replication delay. Our reading, from needing a deliberate wait after each create in the lab runs rather than from a measurement of the failure itself: an object created seconds earlier can 404 on a read that lands on a different replica. Retry with a short backoff rather than treating the first 404 as final.
  5. If you are using $expand, remove it and retry. If the bare request succeeds and the expanded one 404s, the missing object is the reference target, not the object you asked for. A deleted manager, owner or group member is the usual cause.
  6. Confirm you are on the right tenant. An object ID from one tenant is meaningless in another, and the error looks identical to a typo. Compare the tid claim in the token you are using with the tenant you think you are calling.

Sources

  1. Azure AD Graph error codes and error handling (retired)
  2. Microsoft Graph error responses and resource types
  3. Microsoft Graph permissions reference