Yes, you can do this in ways other than conditions. However, I disagree with basically every objection you raise.

First, the decision made by the handler case doesn't necessarily care which file the error was in, what the line number is, etc. All I've ever needed to decide what to do (details below) was the text content of the malformed record. From the perspective of my program, there were only a few possible cases:

1. The record is malformed in a way that I know how to recover from. In that case, I can just do so and invoke the `use-instead` recovery path.
2. The record is damaged in a new and exciting way. I can log it and try to muddle on, in hopes of catching all the new error cases while I'm off doing more interesting things than waiting on a 6-hour job.
3. The record is damaged irrecoverably and future records depend on it. (e.g., the file structure itself is damaged and this can't be recovered from). This is the rarest case I've come across, but also the only one that's convenient to handle with exceptions.

Further, if it was just a filename and byte offset that was needed to resume where I left off, you may have a point. However, suppose that there was an additional decompression step involved. You can't, with most decompression libraries, pick up decompression in the middle of a stream, at least not without littering knowledge of the decompression through the entire process. Further, bundling everything necessary to pick up the computation where it left off forces you to structure your code in a certain way. For example, packing the state of the computation into a class with member functions doing each part, so that the file, current list of results, etc, are essentially scoped globals. I estimate that this would have been at least 5x more code than what was essentially wrapping a stream with a couple of transformers and iterating over it.

To your third point, I could have structured the parser to call a callback with the details of the problem, which could throw an appropriate exception to unwind to a suitable recovery point. This is, after all, how conditions are implemented. However, conditions as part of the language mean that every error can have recovery paths registered, not just ones that the developers thought to provide callbacks for.

And finally, to your second point. While I originally stole the example from Practical Common Lisp, I've since had exactly this situation come up. I had a ~150GiB file containing, essentially, lines of JSON. My parser validated that the incoming JSON fit a schema and processed it into a more compressed form such that I could fit the aspects of the dataset that I actually cared about into RAM. Now, this dataset had been through several migrations, between a number of different platforms, and not all of the migrations were bug-free. In some cases, it treated UTF-8 as CP-1251 and transcoded that into UTF-8. Others got double-escaped. Still others had parts of some fields duplicated in ways that were easy to detect and undo. Some records were just duplicated outright, and some were different versions of the same record. All of these were recoverable, but it was 150GiB of data. I couldn't manually clean it first; I needed to run the program to see what it barfed on in order to fix it. Worse, being JSON, it compressed easily and this was at a time when 150GiB was more than half the disk space I had available to me. So of course the dataset was compressed on disk, and I was decompressing it as I read it.

Now, I'm sure that you can come up with a way that I could have packed the error recovery into the callback in the inner loop of the iterator, but why would I have? I had conditions at my disposal, and the way I actually did write it, I had the happy path in a perfectly clear straight line, and all of the various error cases and how to handle them lined up in a row next to it. The code was easy to read and work with, without any real efficiency cost. The fact that I could have made do with callbacks is no more relevant than that I could have made do using goto instead of loops and functions: we have these abstractions so that we can express what we want our programs to do at a higher level.