I could come up with quite a few scenarios where you would want the job to be considered failed if there was no work to process. I'm sure you could come up with just as many scenarios where you would want it to be considered successfully completed. The real question here is what the default should be. In general I dislike no-oping and completing successfully rather than throwing an exception. We had many cases in the past where we no-oped and it caused confusion. For example, if someone expects to have data processed in a run but the job completes successfully with nothing having been written, you'll usually get something to the effect of: "Spring Batch won't process any of my records for some reason, this has to be a bug!" Rather than someone realizing that the file was empty, or the query was wrong. However, if you throw an explicit exception it's pretty obvious what happened. I suppose that's the brunt of my issue, if you no op and complete successfully there's no way to know that there was no work processed (other than the item count). However, if you throw an exception, someone can choose to ignore it (it's fairly easy to swallow it) if it doesn't make sense for their scenario. Furthermore, I would want to be able to log out the fact that nothing was done as well, without an exception I have no way of doing that without making my item reader stateful.
I'm still open to discussion in general, but having been burnt in the past with no-oping, I'm hesitant to do it. It still feels to me that without an exception you're losing some vital information about the job run, with no good way of accessing it.