1

Some of the checks I make to validate http requests involve making a call to an external service to parse part of the request body. These calls can fail because of network connectivity issues. My feeling is that I should return a 5xx error to indicate that the client may want to try again later.

However, what do I do if there's a network problem when performing one check, AND another check that doesn't need the network connectivity fails (e.g. the client sends in a string where a number is required)? Should I return a 4xx if I expect the network error to be transitory (giving the client a chance to fix the client error while the server fixes the server error), and a 5xx otherwise to tell the client not to bother trying with a different input immediately? Does it depend on the specific types of 4xx and 5xx I would have returned? Is there a standard solution?

I did come across What HTTP status code to return if multiple actions finish with different statuses? where one of the answers suggests using a 207 "Multi-Status" code but this seems strange (to me at least--aren't 2xx codes supposed to indicate some sort of success?).

Update

I want to give a few more specifics about my particular case. I'm working on updating an existing REST service that creates a long-running (potentially days) batch process. Before a batch process is created, the service does validation of the POST request parameters.

Some of the parameters in the POST request are easy to validate (e.g. is a string non-empty, can the string be parsed as a date). One of the parameters, however, is supposed to be a string in DSL. I can validate this DSL string by sending it on to another REST service and seeing what I get back. I'm not exactly sure how reliable this validation service is, but I suspect that there's a reasonable chance that it could be down for a few hours at a time AND there's a reasonable chance that when it is down it is only down for a few seconds.

I'm expecting that crafting the DSL strings correctly is going to be significantly more difficult than changing a string to a number for end users. End users may not want to spend time working on fixing their DSL string if the service is likely down for the rest of the day.

All this said, I guess I really have a set of general questions:

  • Even in situations like mine, are there any straightforward answers/rules of thumb like, maybe, "503 trumps 400", or "400 trumps 500", or "use this obscure code"?
  • If I'm able to make guesses for how long a service is going to going to be down, or what the costs in terms of user experience, network bandwidth, debugging time, etc., are going to be, should that affect my the codes I return? If so, are there any good heuristics for making these guesses? What should be on my checklist of things to consider?
  • Are there other possible solutions? @coteyr suggested forcing the client to make multiple calls, which might work. I'm also considering using a 400, but adding a warning line in the response body saying that one of the errors might be on the server side. My 400 response bodies already try to list the specific client errors.

3 Answers 3

7

If you know that the same request is always going to fail even if network/other server errors are fixed, return 4xx. There's no point returning a 5xx response, having the client retry only for you to return 4xx a bit later.

If you don't know whether the request would be acceptable, return 5xx.

3
  • "There's no point returning a 5xx response, having the client retry only for you to return 4xx a bit later."--seems like you could have just as easily have written "There's no point returning a 4xx response, having the client retry only for you to return 5xx a bit later". I don't want the client potentially to go through the trouble of correcting their request (e.g. resetting their password, or reading the documentation) only to have it turn out that the service is going to be down for the next three days. Commented Oct 18, 2022 at 12:45
  • 1
    @BenjaminBerman A 5xx might fix itself in the next few seconds. A 4xx never will. Commented Oct 18, 2022 at 12:55
  • Yes - you should prioritize behavior over incident. An outage exists in time-domain (is subject to all sort of causes and may have a mitigation process) whereas behavior is Boolean pass/fail (and your interpretation of "intent" is never guaranteed - which is why we shouldn't attempt automated correction). Commented Oct 18, 2022 at 19:46
3

You should probably return a 4xx code in that scenario.

A 4xx status code represents that the client made an error or otherwise invalid request. Generally, re-sending the request will not change the response.

A 5xx status code indicates that the server is aware that it has made an error or is otherwise unable to fulfil the request. The error may be temporary or persistent.

So lets think about how a reasonable client would react when receiving such errors:

  • For a 4xx error, they know that it won't make sense to re-send the request as-is. Maybe the client can fix the problem and then resend.
  • For most 5xx errors, it makes sense to re-send the request as-is, possibly after some waiting period.

You already know that your server will eventually respond 4xx to that request, the only difference being the exact contents of the response describing the errors (if any). So, I think it makes sense to respond with a 4xx immediately, without providing an indication about the server error at that point. Otherwise, your server will likely get the same request later, which you already know to contain a client error.

But in practice that might need extra development effort. For most web frameworks, it is simpler to raise an exception when there are problem, which will automatically create a 500 Internal Server Error. I think relying on that behaviour is less helpful than noticing that you can send a 4xx, but in practice it is fine as well.

Do not use 207 Multi-Status. It is not part of core HTTP, but of WebDAV. Thus, most HTTP clients will not be able to support it properly. The Multi-Status response is intended for situations where one HTTP request results in multiple WebDAV responses, e.g. a WebDAV PROPFIND request that returns multiple resources.

2
  • Not all frameworks treat exceptions as 500 Server Errors. Specific exceptions might trigger 4xx errors, if the framework defines such exceptions. But this is highly dependent on the framework. Commented Oct 17, 2022 at 20:20
  • Assume a “poisonous” request that freaks out the server because of a bug in the server code. The request would be valid but always return a 500 status.
    – gnasher729
    Commented Oct 18, 2022 at 8:10
2

If I understand you correctly then you have a code that looks something like:

return 4xx if not_a_number(string)
return 500 if go_get_zip_code_demographcis_from_third_party_api(zip_code)
return 200

The general rule of thumb for a web request life cycle is to do only one unit of work at a time. Now, what I suggest is that you have a design problem and not a "which status code" problem.

If go_get_zip_code_demographcis_from_third_party_api(zip_code) is SO bad that you can't count on it to finish correctly in a very small amount of time (say 20ms) then it might be better to redesign the behavior so that if not_a_number passes then go_get_zip_code_demographcis_from_third_party_api(zip_code) is run out of band. If your building an API service then use call backs.

return 4xx if not_a_number(string)

queue(go_get_zip_code_demographcis_from_third_party_api(zip_code), call_back_ul)
return 200

Then your application is in charge of figuring out what to do about the problematic call.

If you insist on trying to do all the work in the web thread, then fail as fast as you can.

3
  • Totally agree that this is a self-inflicted issue created by combining two operations, thus making status codes ambiguous. There's nothing wrong with forcing the client to make two calls in this situation. The lack of efficiency is far outweighed by the increase in clarity.
    – Dan Wilson
    Commented Oct 18, 2022 at 0:26
  • @coteyr I think my particular situation is a bit different from your because the potential cost of correcting the request is higher. I'm not sure if that makes a difference. I do think the idea of splitting things into multiple calls is probably the way to go (not sure if I'm going to be able to make the API change, unfortunately). Commented Oct 18, 2022 at 13:32
  • @BenjaminBerman then fail the fastest way.
    – coteyr
    Commented Oct 19, 2022 at 2:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.