0

Should an error 500 only be thrown by the web server, or is it acceptable for your program to throw one itself?

try {
  some bad code;
}
catch (Exception) {
  set_header_code(500);
  set_content_type('application/json');
  set_response_body(json_encode({'is_error': true, 'message': 'An unknown error occurred', 'request_id': $random_number}));
}

Is this an anti-pattern?

EDIT

In response to Robert Harvey's comments:

500 Internal Server Error -- The server encountered an unexpected condition which prevented it from fulfilling the request.

Wikipedia -- A generic error message, given when no more specific message is suitable. The general catch-all error when the server-side throws an exception.

I could use this pattern to validate a form:

if ($email_address === null || !is_valid_email($email_address)) {
  throw new Exception('You must supply a valid email address');
}

The server has encountered an unexpected condition which is preventing it from fulfilling the request, namely the email field is missing or invalid.

The code can then catch this error later, converting it into a nice error 500 page with some JSON data that duplicates the error.

Why would you need both error 500 and {is_error: true}? How about if you generate an error 500 and say {is_error: false}? What does that mean? Why not leave it as 200 OK with {is_error: true}?

In JavaScript:

if (http_response_code == 500) {
  // The server did not respond as expected
  // Do not try to parse the response body as JSON
  //   because there's no guarantee it contains JSON data
  // Display default error message
}
else if (http_response_code == 200) {
  response = parse_response_as_json();
  if (response.is_error) {
    display_error_message(response.message);
  }
  else {
    // process response
  }
}
11
  • 1
    Do you plan on ever debugging this, or will it remain an "unknown error" forever? Because the way you've written it essentially buries any useful information you might have had about the original exception and why it might have occurred. Nov 29, 2016 at 23:42
  • @RobertHarvey It's to communicate errors to the user, not the developer. As such, it's unlikely to get fixed quickly, if ever.
    – CJ Dennis
    Nov 29, 2016 at 23:44
  • 1
    You're essentially swallowing the exception, and yes, that is a bad practice. My guess is that if you simply allow the exception to propagate, the web server will oblige with its own 500 internal server error. The difference being, you might actually get a chance to capture some useful information about the exception. Nov 29, 2016 at 23:45
  • @RobertHarvey I've simplified the code somewhat. Our production code also includes a tracking ID which we can use to trace the error. However, I'm asking about if the response code should be overridden like that since some client-side JavaScript checks the response afterwards.
    – CJ Dennis
    Nov 29, 2016 at 23:48
  • 1
    My interpretation is that it's "the general catch-all error when the server-side [code] throws an exception." Wikipedia defines it as "A generic error message, given when an unexpected [error] condition was encountered and no more specific message is suitable," which also works. Most web servers that I know of will return a 500 internal server error when an uncaught exception occurs from any cause, which makes perfect sense to me. Nov 30, 2016 at 0:08

2 Answers 2

4

For form validation errors, a 400 - Bad Request is probably more semantically correct.

From RFC 7231:

6.5.1.  400 Bad Request

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

500 - Internal Server Error is generally used when (and normally interpreted by clients as) "Something bad happened on the server, I don't know what it is (but even if I did, I probably couldn't tell you), and I'm giving up. Sorry it didn't work out."

Of course, nothing prevents you from returning 200 OK, serving an error status in your JSON, and letting the client work it out.

The key here is that a bad email address isn't an exceptional condition. It's simply an input error.

That said, the response code returned by the web server is an implementation detail. The only code that should know about that is the code of the web server itself, and possibly any code that you write which interacts directly with the web server. The rest of your code should not have to ever concern itself with that.

2
  • The code should also provide accurate semantics to the programmer. The code could behave completely correctly (i.e. produce desirable results) but be incredibly difficult to understand. Just because I can program a certain way, doesn't mean I should program that way.
    – CJ Dennis
    Dec 1, 2016 at 0:09
  • Sounds reasonable to me. Dec 1, 2016 at 0:31
2

Using the narrow definition from Wikipedia's article on web servers:

The term can refer to the entire system, or specifically to the software that accepts and supervises the HTTP requests.

(emphasis mine), means that the software that the web server calls outside of itself should not deliberately send error 500, or any other error in the 500 range.

Here's a description of the response code groups in simple language:

  • 2XX: Program says: I followed your instructions as best as I could and here's your response, or a report of why I couldn't do exactly what you asked
  • 3XX: Program or server says: I know what you're asking for, but instead of giving it to you, I'm going to tell you a different way of getting the response
  • 4XX: Program or server says: I can't give you what you're looking for
  • 4XX: Server says: I don't know what you're asking for. Your request was mangled in some way
  • 5XX: Server says: Uh oh! Something really major went wrong. I'm really sorry I can't be more specific than that

Since you don't know how strict or tolerant every client is, you SHOULD use the narrow definition and let the web server send most errors. By definition, a program can't send an error 500 (indicating it's not in a working state) because it has to be in a working state to do so.

The server is in several layers and it's not appropriate for all of them to send any response code:

  • Server hardware
  • Operating system (such as Linux)
  • Web server (such as Apache) - sets a response code for every request
  • Programming language (such as PHP) - causes an error 500 if the code crashes or has an unhandled exception
  • Website code - can override the default response code if the rules are too complex for the server to work out, e.g. error 404, redirect 302, etc.

For example, if your pages don't have a physical location but are generated dynamically, the server doesn't know whether it can serve it with a 200, or it doesn't exist and it should send a 404. There is no other option but to override the code from within your program. However, it is bad practice to send a 500, because your program hasn't crashed at that point. While your program is running, you always have options.

It's especially bad to send two errors that mean the same thing as you're breaking the DRY principle (Don't Repeat Yourself). Your program determined that the request was good, but the data was bad (or some other error). It should exit normally, with a 200 code, and an explanation within the response of what the error was, and what the client or user should do.

TL;DR:

Error 500: big crash. Don't use for small things!

Your Answer

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

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