64

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:

Atif Aziz
  • 36,108
  • 16
  • 64
  • 74
  • Why should a web server care about syntax errors? – leppie Jan 24 '11 at 11:03
  • @leppie: The web server needs to make sure, for example, that the request line and headers are well-formed. – Atif Aziz Jan 24 '11 at 12:01
  • But that would be a malformed client request. – leppie Jan 24 '11 at 12:02
  • 1
    @leppie: A client can always send a malformed request. It's clear that a server would respond with a 400 in that case. What's unclear is whether a 400 is a legitimate response to a well-formed yet invalid (per application) request. – Atif Aziz Jan 24 '11 at 12:22
  • 1
    @Atif Aziz: If the request can be passed to some backing server, then it should have a 500 error, except for well-known client errors like authentication failed or 404. – leppie Jan 24 '11 at 12:24
  • @leppie: That sounds like an answer. :) – Atif Aziz Jan 24 '11 at 12:34
  • As an aside, for future reference: some example code I've seen returns 400 Bad Request when login credentials are wrong. The response body then includes the very same login page again. That might work when sent directly to a browser. But, for example, jQuery Mobile simply shows "Error Loading Page" in such case. – Arjan Mar 22 '12 at 12:05

8 Answers8

65

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.

Julian Reschke
  • 40,156
  • 8
  • 95
  • 98
  • 1
    RFC 4918 is the WebDAV RFC with which I'm not familiar. Is it ok to re-use the [status code extensions to HTTP/1.1](http://greenbytes.de/tech/webdav/rfc4918.html#status.code.extensions.to.http11) in non-WebDAV HTTP services? The section on [HTTP client compatibility](http://greenbytes.de/tech/webdav/rfc4918.html#rfc.section.B) would seem to indicate that it is. Does the IETF maintain a definitive list of all (core and extension) HTTP Codes to ensure that some other service that builds on HTTP doesn't introduce yet more codes that conflict with other extensions? – Day Jan 24 '11 at 19:39
  • 3
    Yes, it is ok. That's why there's a status code registry. See http://www.iana.org/assignments/http-status-codes. – Julian Reschke Jan 24 '11 at 19:49
  • 5
    422 comes very close. The only shame is that it seems specifically concerned with an unprocessable entity as opposed to an unprocessable request. If only it encompassed the latter, it would also cover the case of HTTP GET because it comes with no entity. The description of 422, however, does make it clear that 400 is inappropriate for a logically invalid request. – Atif Aziz Jan 25 '11 at 00:18
19

As of this time, the latest draft of the HTTPbis specification, which is intended to replace and make RFC 2616 obsolete, states:

The 400 (Bad Request) status code indicates that the server cannot or will not process the request because the received syntax is invalid, nonsensical, or exceeds some limitation on what the server is willing to process.

This definition, while of course still subject to change, ratifies the widely used practice of responding to logical errors with a 400.

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
6

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

From https://datatracker.ietf.org/doc/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)"

Community
  • 1
  • 1
Andrei Neculau
  • 986
  • 1
  • 10
  • 15
2

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.

Community
  • 1
  • 1
Day
  • 9,465
  • 6
  • 57
  • 93
  • 4
    That's certainly an interesting way to look at it. What about GET requests where an invalid query string parameter value is being used? Would you say 400 is fine to use because although the query string is well-formed, the query string parameter value has a syntax error per the application's view? – Atif Aziz Jan 24 '11 at 19:12
  • Well it seems a small step from the POST example that I gave, but I'll admit is seems a little icky to do the same with GET requests when the application finds parts of a URL to be invalid. In a **truly** Restful interface using HATEOAS (Hyptertext as the Engine of Application State), URLs for resources are not important for the client to know - clients merely follow links from other resources. But then most "REST" APIs don't stick to this principal and instead explain what query string parameters are required to be appended to what URL, so I guess in such services a 400 response would be ok. – Day Jan 24 '11 at 19:52
2

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

Vish
  • 29
  • 1
1

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".

Mike Braun
  • 3,729
  • 17
  • 15
Panu Logic
  • 2,193
  • 1
  • 17
  • 21
1

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.

Luke Puplett
  • 42,091
  • 47
  • 181
  • 266
0

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);