My two pennies on this topic.
I'd make a clear distinction between messaging stuff, debugging tools and error management.
1. Messaging: prompting warnings, alerts, etc. to the user when something is wrong regarding the context from which s/he runs the script, or similar issues. E.g. "nothing is selected", "no document available" etc. These are not errors in the strict sense (although true errors may require messaging too.) To me, messaging is just an interface issue. It answers the question, how and when should I notify something special to the user? This shouldn't be strongly coupled with error management, because in most cases the bug is the user, not the script. A good script is responsible for detecting usage or context-related failures and should not throw exceptions in such cases (IMHO).
2. Debugging: this is obviously a very broad question, which involves as well testing strategies, code quality tools (JSLint etc.), assertions, logging. All this stuff is clearly intended for the developer. Having in your toolbox a good logging library that you just need to include whenever a project becomes serious is the best approach to me. Logging features are somehow “embedded” in the final script, but they remain silent as long as the log level is zero. On the dev side you use various levels (e.g. trace, warning, error, critical…) during the design, but I suggest logging be also activatable from the user side—based on some hidden switcher—because users are expert in detecting issues. Having a verbose log report of all what happened before your script made ID crash is the most precious thing in the world.
3. Error management: this is the realm of “exceptions” and try-catching. That is, issues that shouldn't normally occur and would lead to runtime errors if they were not managed. ExtendScript provides exception handling and implements the basic Error types required by ECMA's specification. Note that error management is both connected to messaging (1) and debugging (2) but it seems to me very useful to consider it a separate domain. It answers the question, how, when and where do I manage actual errors? One side of error management is throwing custom exceptions. A good practice is to have some flag on your custom Error object so that you can identify that this was not a 'native' error. Exceptions should be thrown as close to where the error would have occurred (in order to recover the maximum amount of contextual data), but opinions are sharply divided about the place from where exceptions should be caught. A global try...catch embedding your whole script makes things very easy to manage, but there are less paranoid solutions. Either way, having a dedicated function (not only a “global variable”) for sending, storing, and receiving errors is, I think, the way to go.
Thanks Marc. You have clarifed what I should be exploring which is messaging. The scripts I am working on are relatively simple. There are numbers of places in scripts where a condition may not be met, sometimes this may mean there is no point in going further, sometimes it means part of the script is not needed (No paragraphs in US English). Having a function for dealing with telling the user what is going on seems better than relying on various alert() messages sprinkled about the script.