Error codes

Error codes

by Philipp Imhof -
Number of replies: 6
Picture of Core developers Picture of Plugin developers

TLDR: Currently, errors 404, 403 or 407 are sent with a moodle_exception. However, the status code is not chosen depending on the error, but rather the client. Unfortunately, 404 errors can lead to IP bans on shared hosting plans, so they should not be used excessively.


While working on MDL-67198 I came across the following issue, that I would like to discuss. (I am new to Moodle Dev, so please correct me, if I got something wrong.)

Whenever a moodle_exception is thrown, the function core_renderer::fatal_error is called. This function will then cause the web server to respond with error status 404, 403 or 407. Which code is chose seems to depend solely on the question whether $_SERVER['HTTP_RANGE'] is empty or whether a certain version of Safari on iOS is used. IMHO it would make more sense to choose the response code according to the error rather than the client.

Also, using 404 as an error code can be considered wrong in certain cases, and borderline in other situations. For example, if one tries to access /course/view.php?id=XXX with an invalid ID, one might argue that 404 is a correct reponse, because the requested course could not be found. (This is in line with RFC 2616 which says that 404 is to be sent when the server "has not found anything matching the Request-URI", and the query string is part of the request URI.) One could also argue that 404 is wrong in the sense that view.php was found and executed, so the error actually only happened at the application level. Thus, the server itself should not send back an error code.) However, if one accesses /course/view.php with no parameters at all, the request URI is valid and the resource is found. In this case, a 404 error is IMHO clearly wrong w.r.t. RFC 2616.

As an aside, 404 errors can trigger nasty IP bans on shared hosting plans, because they might indicate that someone is probing the server.

OTOH, some errors really should lead to a 404 response, e.g. when adding an LTI component, the external server will issue a REST call to Moodle to check whether the same component is already installed. If it is not, 404 is the right thing to send. 

So, I would like to know what the more experienced developers think about the idea that the status code should  be chosen depending on the actual error?

Average of ratings: -
In reply to Philipp Imhof

Re: Error codes

by Mark Johnson -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Hi Phillip,

To add a bit more context to what you have found, core_renderer::fatal_error is called by default_exception_handler, when an exception is thrown but not handled by any catch block. This doesn't mean it will run whenever a moodle_exception is thrown, or only when a moodle_exception is thrown.

Generally, I would agree that sending a 404 error code for every case where an exception is unhandled isn't always going to be correct. However, since this code could be triggered by any uncaught exception, it would be difficult to define a way of indicating the desired error code for every exception. The other error codes used by fatal_error appear to be for special cases where range requests are being made for streamed media, so I assume they have been chosen deliberately.

I don't agree that 404 is an inappropriate error in the case of calling /course/view.php?id=XXX with a non-existant ID. The resource you are requesting is course XXX, and it doesn't exist, so 404 is appropriate.

Unfortunately, we can't really account for every hosting provider's configuration, if your users are encountering enough 404s during normal usage to trigger your host's security heuristics, I'd suggest:

  • Understanding why they are hitting so many 404s and resolving the issue.
  • Talking to your host about changing their heuristics.
  • Moving to a different host that better suits your needs.
In reply to Mark Johnson

Re: Error codes

by Philipp Imhof -
Picture of Core developers Picture of Plugin developers

Hi Mark,

Thank you for the clarifications. I did not want to insinuate that 404 was wrong in the case where the user tries to access an invalid course ID, even less so, because it is in line with RFC 2616. (I should have made that clearer.) I do, however, consider it as wrong in the case where no course ID is provided at all, because then the request URI is valid and the resource is found.

I understand your point that we will probably not be able to send the perfect status code for every uncaught exception. Maybe a good solution would be to change default_exception_handler in order to account for the most important or most common things: 

  • If the exception is of type require_login_exception, required_capability_exception or invalid_dataroot_permissions, the response could be 401 Unauthorized or 403 Forbidden. 
  • For coding_exception it could be 500 Internal Server Error, because the error is not on the client side and they cannot solve the problem on their own. 
  • For invalid_parameter_exception, invalid_response_exception (and maybe invalid_state_exception), it could be 400 Bad Request. The same response code could be used for webservice_parameter_exception, even though that's probably not necessary anymore, because this exception was deprecated long ago. 
  • For file_serving_exception it could be one of 401, 403, 404 or 415 with the correct choice indicated by the code that throws the exception and a reasonable default value.

We could then add a new parameter $http_response to fatal_error with default_exception_handler passing the correct status. If no value is passed, the default could be 404 in order to maintain maximum compatibility with the current behaviour. The special cases for streamed media could still be dealt with.

Unfortunately, 404 errors can currently be generated without the user being able to do anything about it, e. g. when students resume a lesson at a bad point (after having answered the last question in a cluster, but before having clicked "Continue" on the feedback page), see MDL-67369. With just one single class working on a course, you can quickly get tens of 404 errors in a short time which triggers stuff like fail2ban or similar.

In reply to Philipp Imhof

Re: Error codes

by Philipp Imhof -
Picture of Core developers Picture of Plugin developers

Thinking of it again, it seems better (and even easier) to define the HTTP response code when constructing the exception, with the default being as exposed in my other post. Whoever wants to throw an exception has to chose the correct type and the correct error code (in the sense of error.php). At that moment, they should also think about the HTTP response they would want Moodle to send back to the client.

This gives maximum flexibility, as the HTTP response can be chosen individually for every error. OTOH, it is easy and compatible, because there are reasonable default values for the existing code. 

In reply to Philipp Imhof

Re: Error codes

by Mark Johnson -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

It sounds like a sensible proposal to have default_exception_handler pass a different code through to fatal_error depending on the type of exception being thrown. I'd suggest you raise an issue on the tracker detailing this idea, and provide a patch if you can.

Changing each exception class to accept an HTTP code parameter would be much more complicated, and to be honest, I don't think it's appropriate. Exception-throwing code shouldn't be trying to define what should be done in response to that exception, and Moodle code isn't necessarily being run in response to a web request.

In reply to Mark Johnson

Re: Error codes

by Philipp Imhof -
Picture of Core developers Picture of Plugin developers
Thank you very much for your feedback. I will gladly prepare a patch and submit it.
In reply to Philipp Imhof

Re: Error codes

by Michael Milette -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators
Hi Philipp,

Here are my thoughts on the handling of 4xx series errors.

CASE A: If a user tries to access a page with an invalid URL


The user should get a 404 Not Found error regardless of whether the .php file exists or if it is because of something like an invalid course ID in the URL's parameters.

It does not matter because, in the end, there is no valid page to be displayed using the URL as a whole.

One could argue that a correct response might be a 400 Bad Request error, however, this is giving a potential hacker a hint as to the source of the problem which is undesirable.

Therefore I still think a 404 Not Found error is best in these cases.

CASE B: If the URL is valid but the user is not logged in


The user should be redirected to the login page.

If the user cannot access the page because they are not logged in, why would you send them anywhere else? This is simply the next logical step in the process of getting them to where they want to go.

Ideally, the login page would not only offer them to option to login, but also include an explanation as to why they ended up on this page unexpectedly.

If the login page page is unable to display a message indicating the reason why the user was redirected, a status page may need to be inserted in between. However, from an accessibility point of view, this is an extra page of content for someone to read and an extra click is then required - i.e. extra unnecessary steps.

It would therefore be better for all users if the login page had the ability to display a message explaining why the user ended up there unexpectedly.

Clients tell me all the time that Moodle requires too many clicks to get things done so this would help there too.

CASE C: If the URL is valid but the user fails to log in


The user should be redirected to a 401 Unauthorized error page.

This page should offer them a button which would take them back to the login page to try again.

You just want a standard message here, no explanation as to why they failed to login. You do not want to provide potential hackers with the reason why the attempted login failed.

CASE D: If the URL is valid and the user is logged in but they do not have access to the content


The user should be redirected to a 403 Forbidden error page.

This page should offer them a button to go back to the referred page. Sending people back to the home page or something like that when an error is encountered can be very disorienting and should be considered an accessibility issue.

Hope you find this helpful.

Best regards,

Michael Milette
Average of ratings:Useful (1)