Exceptions evolved as a generalization of errors. The first programming language to include an exception mechanism was Lisp in the early 1970s. There's a good summary in A Pattern of Language Evolution by Gabriel and Steele. Exceptions (which were not yet called exceptions) arose from the need to specify the behavior of a program if an error occurs. One possibility is to halt the program, but this is not always helpful. Lisp implementations traditionally have had a way to enter the debugger on an error, but sometimes programmers wanted to include error handling in their program. So 1960s Lisp implementations had a way to say “do this, and if an error occurs then do that instead”. Originally errors came from primitive functions, but programmers found it convenient to deliberately trigger an error in order to skip some part of the program and jump to the error handler.
In 1972, the modern form of exception handling in Lisp appeared in MacLisp:
catch. The Software Preservation Group lists a lot of material on early Lisp implementations, including The MACLISP Reference Manual Revision 0 by David Moon. The primitives
throw are documented in §5.3 p.43.
catch is the LISP function for doing structured non-local exits.
(catch x) evaluates
x and returns its values, except that if during the evaluation of
(throw y) should be evaluated,
catch immediately returns
y without further evaluating
catch may also be used with a econd argument, not evaluated, which is used as a tag to distinguish between nested catches. (…)
throw is used with
catch as a structured nonlocal exit mechanism.
(throw x) evaluates
x and throws the value back to the most recent
(throw x <tag>) throws the value of
x back to the most recent
catch labelled with
<tag> or unlabelled.
The focus is on nonlocal control flow. It's a form of goto (an upward-only goto), which is also called a jump. The metaphor is that one part of the program throws the value to return to the exception handler, and the exception handler catches that value and returns it.
Most programming languages today pack the tag and the value in an exception object, and combine the catching mechanism with a handling mechanism.
Exceptions are not necessarily errors. They're a way to exit from a block of code and from the surrounding blocks, escaping until a handler for the exception is reached. Whether such a thing is considered an ”error“ in the intuitive sense is subjective.
Some languages make a distinction between the terms “error” and “exception”. For example, some Lisp dialects have both
throw to raise an exception (control flow for users, meant to perform a non-local exit in a way that doesn't indicate that anything went “wrong”) and
signal to raise an error (which indicates that something went “wrong” and may trigger a debug event).
XYError-- for instance, in Python.