Exception

On this page 7

An exception is a runtime event that interrupts the normal flow of a program when an operation cannot complete as expected, such as dividing by zero, opening a missing file or receiving invalid input. In programming fundamentals, raising an exception transfers control to the nearest matching handler up the call stack instead of returning a value to the caller.

In most modern languages an exception is also a value, usually an object that carries a type, a message and a stack trace. The mechanism separates the code that detects a problem from the code that knows how to respond, which is the central idea of structured error handling.

How an exception travels through the call stack

When code throws or raises an exception, the current function stops at that point, and the runtime begins stack unwinding: it leaves the current function, then its caller, then the caller’s caller, until it finds a try block with a matching catch or except clause. If no handler exists anywhere on the stack, the exception becomes unhandled and the program or thread usually terminates with an error report.

Handlers are matched by type. Exception classes form a hierarchy, so a handler for a general type such as IOException also catches its subtypes such as FileNotFoundException. During unwinding, the runtime runs cleanup code: finally blocks in Java, C# and Python, destructors under RAII in C++, and with or using statements that release files, locks and connections.

The table below shows the main keywords that implement this mechanism in widely used languages.

LanguageRaiseHandleGuaranteed cleanup
Javathrowtry / catchfinally, try-with-resources
C#throwtry / catchfinally, using
Pythonraisetry / exceptfinally, with
JavaScriptthrowtry / catch, promise .catch()finally
C++throwtry / catchDestructors (RAII)
Exception keywords in common languages

Types of exceptions

Exceptions are classified by their cause and by whether the program can reasonably recover. Recoverable conditions come from the environment, such as a network timeout or a locked file, and a handler can retry or report them. Programming errors, such as a null reference or an index out of range, indicate a bug and should normally be fixed rather than caught.

Java makes this distinction part of the type system. Checked exceptions must be declared in the method signature or handled, while unchecked exceptions derive from RuntimeException and need no declaration. Most other languages, including C#, Python and Kotlin, treat all exceptions as unchecked.

CategoryTypical examplesUsual response
Input and validationMalformed number, invalid dateReject input and inform the user
Environment and I/OMissing file, network timeoutRetry, fall back or report
Programming errorNull reference, index out of rangeFix the code, do not catch
Resource exhaustionOut of memory, stack overflowUsually terminate safely
Domain rule violationInsufficient balance, duplicate orderCustom exception mapped to a business response
Categories of exceptions and the usual response

Exceptions compared with other error-handling styles

Exceptions are one of several ways to signal failure, and languages choose differently. In C, functions usually return error codes and set errno, which callers must check after every call. Go returns an explicit error value alongside the result, while Rust uses a Result type that the compiler forces the caller to handle and reserves panics for unrecoverable bugs.

StyleExample languagesAdvantageDrawback
ExceptionsJava, C#, Python, JavaScriptClean main path, rich contextHidden control flow
Error codesC, many system APIsSimple and fastEasily ignored by callers
Error valuesGoExplicit at every callRepetitive checking code
Result typesRust, Swift, functional languagesCompiler-enforced handlingMore verbose signatures
Error-signaling styles compared

The key trade-off is visibility against convenience. Exceptions keep the normal path readable and carry context automatically, but any call might throw, so the possible exits from a function are not visible in its code.

Performance and asynchronous code

In languages such as C++, Java and C#, the happy path is nearly free because modern runtimes use table-based or zero-cost exception models. The cost appears only when an exception is actually thrown: building the stack trace and unwinding frames can take microseconds to tens of microseconds, which is far slower than returning a value.

For that reason, exceptions should signal exceptional situations, not normal outcomes. Using exceptions for ordinary control flow, such as detecting the end of a loop, makes code slower and harder to follow. Python is a partial exception to this guideline, since its idiom of trying first and catching later is common and accepted.

Asynchronous code changes the propagation path. In JavaScript, an error inside a promise becomes a rejected promise rather than a synchronous throw, so it must be handled with await inside try/catch or with .catch(); otherwise it surfaces as an unhandled rejection that can crash a Node.js process.

Common mistakes when handling exceptions

Most problems with exceptions come from handling them too broadly or too early. Good handlers catch the narrowest relevant type, at the level of code that can actually decide what to do.

MistakeConsequenceBetter practice
Empty catch blockErrors vanish silentlyLog, rethrow or handle explicitly
Catching the base type everywhereBugs are masked as handledCatch specific types
Losing the original causeRoot cause disappears from logsWrap with the cause attached
Exceptions for normal flowSlow and unclear codeUse return values or checks
Leaking resources on errorOpen files and locks pile upUse finally, with or RAII
Exception anti-patterns and their fixes

Applications should still have a top-level handler that logs unhandled exceptions with their stack traces and returns a safe, generic message to users, so internal details do not leak through error pages.

Explained without jargon

A program follows its instructions step by step. Sometimes a step cannot be done, for example when a file it needs is missing. An exception is the program raising its hand and saying something went wrong here.

Instead of carrying on with bad data, the program stops that task and passes the problem upward. It keeps passing it until it reaches a part of the code that knows what to do. That part might show a clear message, try again later or save the work safely. The point is to deal with problems in one sensible place instead of everywhere at once.

A typical use case

An online store’s checkout service calls an external payment gateway. Occasionally the gateway does not answer within its five-second limit, and the HTTP client throws a timeout exception deep inside the payment module.

The developers do not catch the timeout where it occurs. Instead, the payment module wraps it in a custom PaymentUnavailableException with the order number attached, and the checkout controller catches that specific type. The controller retries once, then marks the order as pending and tells the customer that payment confirmation will follow by email. A top-level handler logs any other unexpected exception with its full stack trace. Customers no longer see generic error pages, and the team can trace each failure to its original cause.