The classic one is with yacc, where your user is looking at a message like "syntax error" instead of "Missing comma, foo.p line 23". IIRC you could improve yacc error messages but you had to modify the grammar, and it usually created even more conflicts and made more bugs.
It's not really even "by default" -- that suggests you could change it from the default. It's generally really hard to stick any custom error messages of any kind into a generated parser. You're often limited to making your grammar accept things that are wrong, and then writing a check that detects those invalid situations in the resulting AST. The parser generator doesn't understand any specifics about your language, so it's not capable of producing useful errors by itself in essentially any situation at all. If you're lucky, you can get "I didn't understand this token at this position; here's a list of other tokens that would have been valid." Meanwhile, with a hand-rolled recursive descent parser, you are writing literally every error message yourself. You can generate an error from anywhere you'd like, using any information you need (local or global!), since it's all just code that you're writing.
I have a large-ish grammar that I spent a lot of time on, and I'll be damned if I don't still sometimes get "Invalid statement." with no useful location information. I even wrote my own parser generator; it's not just a shortcoming of existing tools, it's just really hard to write a generic parser generator that's capable of generating custom and useful errors.
Great notes, thank you! I would like to see this in a blog post sometime comparing with specific examples just how hard it is to get any useful messages from a parser generator and how easy it is when handwritten (which I'm more familiar with).
If you or anyone here happens to write this blog post before I do please send me a link when you do. :)
Useless if the messages are intended for a human who is going to use the feedback to manually adjust something (for example, parsing a programming language and telling the user what they did wrong)
If your parser is parsing something machine-generated where the expected case is success, and failure indicates data corruption/malicious input/a bug somewhere else, then parser generators can be useful.