Exception
Subclass it for your own errors and catch it for "anything went wrong" — it deliberately excludes Ctrl+C and sys.exit().
Exception(*args)
Raised by
raise Exception(msg) — better: a specific subclass
Message
whatever you pass: str(e) is the single arg, or repr(args)
Quick fix
class MyError(Exception): pass — then raise MyError(...)
Watch out
except Exception around a big block hides your own bugs
Demo
Live evaluation
except Exception catches whatever int() raises and reports it. Try something that is not a number.
Try:
Inputs
textstrstring to convert
Code
def to_int(s): try: return int(s) except Exception as e: return f'{type(e).__name__}: {e}' to_int(' 42 ')
Result
42
In Trigger the catch-all turns every failure into a string — convenient, but notice it reports ValueError for '4.2' just as happily as for a typo; the same clause would also swallow a bug in your own code. In Handle, NotFound is caught by the first clause even though it is also an AppError and an Exception: order matters, specific first.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| *args | object | no | Usually one message string. All arguments are kept in e.args; no keyword arguments. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| args | tuple | Constructor arguments. A custom __init__ should pass the message to super().__init__ so args and str(e) stay meaningful. |
| __cause__ | BaseException | None | Set by raise MyError(...) from original — keeps the low-level error in the traceback. |
| __context__ | BaseException | None | The exception being handled when this one was raised. |
| __notes__ | list[str] | Notes added with add_note() (3.11+). |
Common patterns
One base class per library
Callers can catch everything from your package with one except, or a single case precisely.
class PaymentError(Exception): """Base class for this package.""" class CardDeclined(PaymentError): pass class GatewayTimeout(PaymentError): pass
Custom exception with data
Pass a readable message to super().__init__ and keep machine-readable details as attributes.
class HTTPError(Exception): def __init__(self, status, url): super().__init__(f'{status} for {url}') self.status = status self.url = url
Catch-all at the boundary only
A broad except Exception belongs at the top of a request, job or thread — log with the traceback and move on.
for job in jobs: try: job.run() except Exception: logger.exception('job %s failed', job.id)
Examples
1. Custom exception in two lines
class QuotaExceeded(Exception):
pass
try:
raise QuotaExceeded('limit is 100 requests')
except QuotaExceeded as e:
r = str(e)
r
Returns
'limit is 100 requests'2. Uncaught custom exception
class QuotaExceeded(Exception):
pass
raise QuotaExceeded('limit is 100 requests')
Returns
QuotaExceeded: limit is 100 requests3. Most built-in errors are Exceptions
[issubclass(c, Exception) for c in (ValueError, KeyError, OSError, KeyboardInterrupt)]
Returns
[True, True, True, False]4. Catching the base catches subclasses
class AppError(Exception): pass
class NotFound(AppError): pass
try:
raise NotFound('user 7')
except AppError as e:
r = f'{type(e).__name__}: {e}'
r
Returns
'NotFound: user 7'5. Forgetting super().__init__ loses args
class BadError(Exception):
def __init__(self, code):
self.code = code
e = BadError(404)
(e.args, str(e))
Returns
((404,), '404')6. Several args show as a tuple
str(Exception('not found', 404))
Returns
"('not found', 404)"7. Keyword arguments are rejected
Exception(message='x')
Returns
TypeError: Exception() takes no keyword argumentsPitfalls
1. A wide except Exception hides bugs
The handler was meant for bad input, but it also catches the NameError from using the wrong variable name — the function silently returns the fallback forever.
Wide catch-all
def price(item): try: return round(float(item['price']) * 1.2, 2) except Exception: return 0.0 def total(items): try: return sum(price(i) for i in cart) # wrong name except Exception: return 0.0 total([{'price': '10'}])
0.0
Specific exceptions
def price(item): try: return round(float(item['price']) * 1.2, 2) except (KeyError, ValueError): return 0.0 def total(items): return sum(price(i) for i in cart) # wrong name total([{'price': '10'}])
NameError: name 'cart' is not defined
2. Raising plain Exception
Callers cannot catch a generic Exception precisely — they are forced into a catch-all. Raise a specific built-in or your own subclass.
raise Exception
def load(path): raise Exception('file is empty') try: load('a.csv') except ValueError: r = 'handled' r
Exception: file is empty
raise a subclass
class EmptyFileError(ValueError): pass def load(path): raise EmptyFileError('file is empty') try: load('a.csv') except ValueError: r = 'handled' r
'handled'
3. Broad handler listed first
except clauses are checked in order. A general clause above a specific one makes the specific one dead code.
General first
try: {}['id'] except Exception: r = 'generic' except KeyError: r = 'missing key' r
'generic'
Specific first
try: {}['id'] except KeyError: r = 'missing key' except Exception: r = 'generic' r
'missing key'
When to use
Use it
- Base class for your own exception hierarchy
- Catch-all at a boundary: top of a request handler, worker loop, CLI main — with logging
- isinstance(e, Exception) to tell ordinary errors from Ctrl+C / exit
Reach for something else
- raise Exception(...) → raise ValueError, TypeError or your own subclass
- except Exception around a few lines where you know what can fail → catch that
- Catching everything including Ctrl+C → you almost never want that (BaseException)
Notes
CPython impl
Objects/exceptions.c — Exception adds nothing to BaseException; it exists to separate errors from exit/interrupt signals
Not caught by it
KeyboardInterrupt, SystemExit, GeneratorExit, BaseExceptionGroup (with non-Exception members)
Naming
By convention custom exception names end in Error (PEP 8)
Logging
logger.exception(msg) inside an except block logs the message plus the full traceback
FAQ
Subclass Exception (or a more specific built-in such as ValueError): class InvalidOrder(Exception): pass. Raise it with raise InvalidOrder('quantity must be positive'). If you add an __init__, pass the message on with super().__init__(message) so e.args and str(e) work.