I think the truly "best" practice would be to use an exception class hierarchy and throw exceptions upon errors. I <3 Exception::Class
Agreed, but that was not the point of this guideline.
To be fair to
TheDamian, he dedicated a whole chapter to error handling, where among other things he discusses exception hierarchies in depth.
Damian's point in this guideline was, one should use an empty return statement where returning undef could lead to unexpected results, while my main point was that he failed to properly distinguish that exotic case from good uses of returning undef, seeing that now some programmers think it should be avoided altogether.
Personally, I prefer to use empty return statements only for void context, i.e. subroutines or methods that don't offer a return value.
If I found myself catering for context-aware return value polymorphism I would not mind to use a more explicit construct.