Request_BadRequest: Microsoft Graph rejected the request itself
Short answer: Request_BadRequest means Microsoft Graph refused the request because of its content or shape: a missing required property, a value that conflicts with an existing object, or a method the target does not allow. The message names the specific problem. It is also the code returned with HTTP 405, which is worth knowing if you match on status codes.
Fix: Read the message; it names the property or the conflict. A missing password on user creation needs a passwordProfile. A duplicate userPrincipalName needs a different value. A method-not-allowed message means you sent the verb to a collection where it belongs on a single object. Full steps
| Error code | Request_BadRequest |
|---|---|
| HTTP status | 400 |
| In Microsoft's Graph error documentation | No (checked ). The wording on this page is what Graph returned in the lab. |
| Category | Graph request shape |
| Attack signal | Low: usually configuration or user error |
What does Request_BadRequest mean?
Section titled “What does Request_BadRequest mean?”Graph understood the URL and refused the request on its content. The retired Azure AD Graph catalogue — the only Microsoft page that defines this code — describes it as “Indicates an error in the request, such as an invalid property value or an unsupported query argument. Fix the request before retrying.” and gives the message “Bad request. Please fix the request before retrying.”
That generic message is not what the service returns. In every case measured below, Graph replaced it with a sentence naming the actual problem. Microsoft’s current Graph error reference lists HTTP status codes only and has no named-code table, so the wording on this page is what Graph v1.0 returned in a lab tenant on 31 October 2026.
Reproduced in the lab
Section titled “Reproduced in the lab”| Request | HTTP | Message |
|---|---|---|
POST /users with displayName only |
400 | A password must be specified to create a new user. |
POST /users reusing an existing userPrincipalName |
400 | Another object with the same value for property userPrincipalName already exists. |
DELETE /users (the collection) |
405 | Specified HTTP method is not allowed for the request target. |
POST /users with the password password |
400 | The specified password does not comply with password complexity requirements. Please provide a different password. |
POST /users with a userPrincipalName on an unverified domain |
400 | The domain portion of the userPrincipalName property is invalid. You must use one of the verified domain names in your organization. |
GET /users?$skip=1 |
400 | '$skip' is not supported by the service. |
PATCH /users/<id> setting accountEnabled to the string "yes" |
400 | Cannot convert the literal 'yes' to the expected type 'Edm.Boolean'. |
PATCH /users/<id> setting a property that does not exist |
404 | Resource '' does not exist or one of its queried reference-property objects are not present. |
PATCH /users/<id> setting the read-only id property |
204 | (no error; a read-back confirmed the id was unchanged) |
A duplicate userPrincipalName returns this code, not Request_MultipleObjectsWithSameKeyValue
Section titled “A duplicate userPrincipalName returns this code, not Request_MultipleObjectsWithSameKeyValue”The retired catalogue lists a dedicated code for exactly this situation: Request_MultipleObjectsWithSameKeyValue, HTTP 409, described as “This error can occur when there are two or more objects that have the same key value, e.g. when two users have the same UserPrincipalName.”
The lab run created a user, waited ten seconds, and tried to create a second user with the same userPrincipalName. The response was HTTP 400 Request_BadRequest, not 409. Whether anything on Graph v1.0 still returns Request_MultipleObjectsWithSameKeyValue was not established; the lab did not produce it. Our reading of the difference: the documented code describes a directory that already contains a conflict, while the lab tested a create that would produce one, and those are not the same operation. Either way, if your code is branching on 409 to detect a duplicate UPN on create, it is branching on something that did not happen here.
Two rows where Graph accepted or misreported the request
Section titled “Two rows where Graph accepted or misreported the request”Two of those rows are more interesting than the messages they carry.
A PATCH setting the read-only id property returned 204 No Content, and a read-back of the object afterwards confirmed the id was unchanged. Graph accepted the request, reported success, and did nothing. Our reading: unwritable properties in a PATCH body are discarded rather than rejected, the same behaviour as $select naming a property that does not exist. The practical rule needs no interpretation though: a 2xx from Graph does not mean the change was applied. Read the object back.
A PATCH setting a property that does not exist returned 404 with Resource ''. Not a 400 about the property, and not a message naming it: a not-found error about an empty resource name. The identifier in the URL was a real user that existed. We cannot explain this from the response, and it is the single most misleading error in these runs — someone debugging it would reasonably go looking for a missing user. If you get a 404 from a PATCH whose URL you have verified, check the body’s property names before anything else.
'$skip' is not supported by the service. Worth knowing because $skip is standard OData and is supported on some other Graph workloads, though not tested here. On directory objects it does not work at all, and paging has to go through @odata.nextLink; the same request without $skip returned 200 in the lab. There is no page-number parameter to fall back on.
The code appears with HTTP 405 as well as 400
Section titled “The code appears with HTTP 405 as well as 400”DELETE /users returned HTTP 405 Method Not Allowed with the code Request_BadRequest. The status and the code disagree about what kind of problem this is.
This matters if you treat the error code as a proxy for the status, or the other way around. Matching only on Request_BadRequest will pull in a 405; matching only on status 400 will miss one. The message is unambiguous in this case, so read it.
Is it an attack signal?
Section titled “Is it an attack signal?”Low. These are the errors of a client being written or a payload being built wrong, and they stop as soon as the code is right.
One small exception worth noting rather than alerting on: the duplicate-value message confirms that an object with that userPrincipalName exists, which is a yes-or-no existence check obtained from a create attempt rather than a read. Whether it works for an app that can create but not read users was not tested — the lab token could do both. It is a weak primitive either way, because an app that can create users in your tenant is already a serious problem.
How do I fix it?
Section titled “How do I fix it?”A password must be specified to create a new user. A user create needs accountEnabled, displayName, mailNickname, userPrincipalName and a passwordProfile:
{ "accountEnabled": true, "displayName": "Alex Taylor", "mailNickname": "alext", "passwordProfile": { "password": "<generated>", "forceChangePasswordNextSignIn": true }}Generate the password rather than using a fixed one, and do not log it. If you are creating users from a script, forceChangePasswordNextSignIn: true means the value only has to survive until first sign-in.
Another object with the same value for property userPrincipalName already exists. The value is taken. Note that it can be taken by a soft-deleted object: per Microsoft’s documentation on deleted items, a directory object stays recoverable for 30 days, and a user in that state still holds its userPrincipalName. Check GET /directory/deletedItems/microsoft.graph.user before concluding the name is free. If the soft-deleted object is the one you actually wanted, restore it with POST /directory/deletedItems/<id>/restore rather than creating a new user. If it is not, the simplest answer is a different userPrincipalName, since the old one frees itself when the 30-day window ends. Permanently deleting the soft-deleted object does release the name immediately, but it cannot be undone and it destroys the object along with anything keyed to it, so confirm what the object is before going near that. The same name conflict applies to mailNickname and proxyAddresses.
Specified HTTP method is not allowed for the request target. The verb does not apply to that URL. DELETE and PATCH belong on a single object (/users/<id>), not on the collection (/users). If you built the URL by string concatenation, check whether the identifier was empty — an empty variable turns an object URL into a collection URL, and this message is often the first sign of it.
The specified password does not comply with password complexity requirements. The tenant’s password policy rejected the value. Generate a long random password rather than tuning a human-readable one to pass; the user changes it on first sign-in anyway if you set forceChangePasswordNextSignIn.
The domain portion of the userPrincipalName property is invalid. The domain after the @ must be one of the tenant’s verified domains. GET /domains lists them with an isVerified flag. The tenant’s onmicrosoft.com domain always works and is the right choice for test accounts.
Cannot convert the literal 'yes' to the expected type 'Edm.Boolean'. A JSON type mismatch. accountEnabled takes true, not "true" and not "yes". This usually means values came from a CSV or a form, where everything is a string; coerce before serialising.
A 404 from a PATCH whose URL is correct. Check the property names in the body. In the lab an unrecognised property produced a 404 about an empty resource rather than an error naming the property.
For any other message under this code, the message is the fix. Graph names the property or the constraint. If it names a property you did not send, check for a typo that made your intended property an unknown one, since an unrecognised property in a POST body is ignored rather than reported.
Related
Section titled “Related”BadRequest— the bare form, returned when the URL or body is rejected before the content is readRequest_UnsupportedQuery— the request was fine but the query parameters were not- All Graph error codes