In most programming languages, “exception handling” is a specific way to distinguish, communicate and handle errors. So while, in a good design, all exceptions should be errors, not all errors must be raised as an exception.

An exception is an object that contains all information about an error (often with the class of the object identifying the general type of error), and a throw command can be used to abort execution of the current function/method and hand control back to each calling function until one has a catch section that will then handle the error (by re-trying, displaying an error message to the user, taking another code path, or whatever else makes sense).

That in turn implies that exceptions are an alternate way of exiting a function or method and providing a kind of return value.

The advantage of exception handling is that inner code can provide errors, while outer (often the top-level) code can react to them. This e.g. allows code that does not know about GUIs to raise an error, and the GUI that triggered it can display the error message.

It also means you can provide better error messages to your users, because you know whether this “disk full error” was triggered because you were trying to write into a cache (in which case you could just flush the cash quietly) or an actual file access to e.g. a compound document (in which case you might want to rewrite the error message to reference the containing folder package instead of the internal files that make up parts of the document).

Finally, exceptions allow you to avoid littering your code with checks for error results after every line, and don’t get deeply nested if statements in all your functions. You can have one try block in your function at the end to clean up after it, instead of one (with possibly duplicated code) after each line.