[http] HTTP 400 (bad request) for logical error, not malformed request syntax

The HTTP/1.1 specification (RFC 2616) has the following to say on the meaning of status code 400, Bad Request (§10.4.1):

The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.

There seems to be a general practice among a few HTTP-based APIs these days to use 400 to mean a logical rather than a syntax error with a request. My guess is that APIs are doing this to distinguish between 400 (client-induced) and 500 (server-induced). Is it acceptable or incorrect to use 400 to indicate non-syntactic errors? If it is acceptable, is there an annotated reference on RFC 2616 that provides more insight into the intended use of 400?

Examples:

This question is related to http http-status-codes http-status-code-400

The answer is


Status 422 (RFC 4918, Section 11.2) comes to mind:

The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad Request) status code is inappropriate) but was unable to process the contained instructions. For example, this error condition may occur if an XML request body contains well-formed (i.e., syntactically correct), but semantically erroneous, XML instructions.


In my case:

I am getting 400 bad request because I set content-type wrongly. I changed content type then able to get response successfully.

Before (Issue):

ClientResponse response = Client.create().resource(requestUrl).queryParam("noOfDates", String.valueOf(limit))
                .header(SecurityConstants.AUTHORIZATION, formatedToken).
header("Content-Type", "\"application/json\"").get(ClientResponse.class);

After (Fixed):

ClientResponse response = Client.create().resource(requestUrl).queryParam("noOfDates", String.valueOf(limit))
                .header(SecurityConstants.AUTHORIZATION, formatedToken).
header("Content-Type", "\"application/x-www-form-urlencoded\"").get(ClientResponse.class);

HTTPbis will address the phrasing of 400 Bad Request so that it covers logical errors as well. So 400 will incorporate 422.

From https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-18#section-7.4.1
"The server cannot or will not process the request, due to a client error (e.g., malformed syntax)"


This is difficult.

I think we should;

  1. Return 4xx errors only when the client has the power to make a change to the request, headers or body, that will result in the request succeeding with the same intent.

  2. Return error range codes when the expected mutation has not occured, i.e. a DELETE didn't happen or a PUT didn't change anything. However, a POST is more interesting because the spec says it should be used to either create resources at a new location, or just process a payload.

Using the example in Vish's answer, if the request intends to add employee Priya to a department Marketing but Priya wasn't found or her account is archived, then this is an application error.

The request worked fine, it got to your application rules, the client did everything properly, the ETags matched etc. etc.

Because we're using HTTP we must respond based on the effect of the request on the state of the resource. And that depends on your API design.

Perhaps you designed this.

PUT { updated members list } /marketing/members

Returning a success code would indicate that the "replacement" of the resource worked; a GET on the resource would reflect your changes, but it wouldn't.

So now you have to choose a suitable negative HTTP code, and that's the tricky part, since the codes are strongly intended for the HTTP protocol, not your application.

When I read the official HTTP codes, these two look suitable.

The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource. This code is used in situations where the user might be able to resolve the conflict and resubmit the request. The server SHOULD generate a payload that includes enough information for a user to recognize the source of the conflict.

And

The 500 (Internal Server Error) status code indicates that the server encountered an unexpected condition that prevented it from fulfilling the request.

Though we've traditionally considered the 500 to be like an unhandled exception :-/

I don't think its unreasonable to invent your own status code so long as its consistently applied and designed.

This design is easier to deal with.

PUT { membership add command } /accounts/groups/memberships/instructions/1739119

Then you could design your API to always succeed in creating the instruction, it returns 201 Created and a Location header and any problems with the instruction are held within that new resource.

A POST is more like that last PUT to a new location. A POST allows for any kind of server processing of a message, which opens up designs that say something like "The action successfully failed."

Probably you already wrote an API that does this, a website. You POST the payment form and it was successfully rejected because the credit card number was wrong.

With a POST, whether you return 200 or 201 along with your rejection message depends on whether a new resource was created and is available to GET at another location, or not.

With that all said, I'd be inclined to design APIs that need fewer PUTs, perhaps just updating data fields, and actions and stuff that invokes rules and processing or just have a higher chance of expected failures, can be designed to POST an instruction form.


It could be argued that having incorrect data in your request is a syntax error, even if your actual request at the HTTP level (request line, headers etc) is syntactically valid.

For example, if a Restful web service is documented as accepting POSTs with a custom XML Content Type of application/vnd.example.com.widget+xml, and you instead send some gibberish plain text or a binary file, it seems resasonable to treat that as a syntax error - your request body is not in the expected form.

I don't know of any official references to back this up though, as usual it seems to be down to interpreting RFC 2616.

Update: Note the revised wording in RFC 7231 §6.5.1:

The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

seems to support this argument more than the now obsoleted RFC 2616 §10.4.1 which said just:

The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.


On Java EE servers a 400 is returned if your URL refers to a non-existent "web -application". Is that a "syntax error"? Depends on what you mean by syntax error. I would say yes.

In English syntax rules prescribe certain relationships between parts of speech. For instance "Bob marries Mary" is syntactically correct, because it follows the pattern {Noun + Verb + Noun}. Whereas "Bob marriage Mary" would be syntactically incorrect, {Noun + Noun + Noun}.

The syntax of a simple URLis { protocol + : + // + server + : + port }. According to this "http://www.google.com:80" is syntactically correct.

But what about "abc://www.google.com:80"? It seems to follow the exact same pattern. But really it is a syntax error. Why? Because 'abc' is not a DEFINED protocol.

The point is that determining whether or not we have a 400 situation requires more than parsing the characters and spaces and delimiters. It must also recognize what are the valid "parts of speech".


Even though, I have been using 400 to represent logical errors also, I have to say that returning 400 is wrong in this case because of the way the spec reads. Here is why i think so, the logical error could be that a relationship with another entity was failing or not satisfied and making changes to the other entity could cause the same exact to pass later. Like trying to (completely hypothetical) add an employee as a member of a department when that employee does not exist (logical error). Adding employee as member request could fail because employee does not exist. But the same exact request could pass after the employee has been added to the system.

Just my 2 cents ... We need lawyers & judges to interpret the language in the RFC these days :)

Thank You, Vish