XMLHttpRequest is always calling "load" event listener, even when response has error status

This setup should work better for your needs:

var req = new XMLHttpRequest();
req.open('POST', '/upload_image');
req.onreadystatechange = function (aEvt) {
  if (req.readyState == 4) {
     if(req.status == 200)
      alert(req.responseText);
     else
      alert("Error loading page\n");
  }
};
req.send(formData);

In your code error callback is never called because it is only triggered by network-level errors, it ignores HTTP return codes.


The load event is called whenever the server responds with a message. The semantics of the response don't matter; what's important is that the server responded (in this case with a 500 status). If you wish to apply error semantics to the event, you have to process the status yourself.


Expanding on @rich remer's answer, here's how you could access the status yourself:

function upload_image() {
    var form = document.getElementById('upload_image_form');
    var formData = new FormData(form);

    var xhr = new XMLHttpRequest();
    xhr.addEventListener("load", function(e) {
        if(e.currentTarget.status < 400)
            alert("Load callback - success!");
        else
            alert("Load callback - error!");
    }, false);
    xhr.addEventListener("error", function(e) {
        alert("Error callback");
    }, false);
    xhr.open("POST", "/upload_image");
    xhr.send(formData);
}

Please note accessing of the e.currentTarget.status property of the response event (e). Looks like the status is actually available via any of e.{currentTarget,target,srcElement}.status - I'm not sure which one should be used as the best practice, though.