IMHO better error messages start with less architecture. When we try to optimize the code reuse, we obtain a split between the code that detect the error but do not have any cue about the action the user is performing and the caller that will receive a generic error message (string) with no way to do something clever without breaking abstraction.