This is a great question and I'll answer it with a concrete example from my own experience. In an API for one of my projects, I allow the upload of an Excel spreadsheet, which is processed and the resulting JSON is stored on the server.
If there is an error saving the data to disk, then I would respond with a JSend "error" along with the appropriate error message since the data should have been able to be saved.
If, on the other hand, the data in one of the rows is not valid (maybe an incorrect data type or a range error), then I know the exact row (or rows) of the spreadsheet that were incorrect. In this case, a "fail" response is appropriate as the data
property of the JSend response will contain a list of all rows (the row number and the error message) for each unprocessable row.
In the case of an "error" response, I wouldn't have that ability as I would be limited to a single message
property. But with the "fail" response, I have the data
property available where I can respond with a fine-grained list of issues.
So, while there was no error, the data itself was not correct and the user should go back and look at their spreadsheet and fix the issues identified by the data
property in the "fail" response.
Thinking of this in terms of HTTP errors, although an interesting exercise, doesn't always result in an exact mapping (4xx = fail, 5xx = error). It's more about what you want to communicate to the client: something bad happened that shouldn't have happened ("error") or the server is working fine but your data isn't quite up to standards ("fail").
Finally, whether you want to also use HTTP errors is entirely up to you. You could always respond with a 200 and let JSend do the talking. But that's a slightly different (and somewhat religious) discussion. :-)
I hope that helps.