Python Bare Except vs Exception: What Each Catches
python bare except vs exception: Understand the difference between bare except and except Exception in Python, and why the bare form hides critical failures like Keybo...
The python bare except vs exception distinction comes down to one rule: a bare except: catches BaseException, while except Exception catches only the Exception hierarchy. That single difference determines whether your handler receives KeyboardInterrupt, SystemExit, and GeneratorExit — exceptions that most application code should let propagate.
What a Bare Except Catches
When you write:
try: run_task() except: log_failure()
Python treats the bare except as except BaseException. The handler runs for every exception type in the language, including ones that normally should terminate the process. Pressing Ctrl+C during run_task() raises KeyboardInterrupt, which is a subclass of BaseException but not of Exception. The bare handler intercepts it, logs it as a failure, and execution continues as if nothing happened.
The same applies to SystemExit, which is raised by sys.exit(). A bare except swallows the exit request, so a script that was supposed to terminate cleanly keeps running. GeneratorExit behaves similarly during generator finalization.
What except Exception Catches
try: run_task() except Exception: log_failure()
Exception is the base class for most errors that application code is expected to handle: ValueError, TypeError, KeyError, IOError, RuntimeError, and so on. It deliberately excludes the three BaseException subclasses that represent control flow rather than ordinary errors.
This makes except Exception the safer default. It still catches a broad range of failures, but it lets process-level signals and exit requests propagate. A user pressing Ctrl+C interrupts the program as expected, and sys.exit() still terminates the process.
Why the Difference Matters in Practice
Consider a long-running worker that processes a queue:
while True: try: item = queue.get() process(item) except: time.sleep(1)
If an operator sends a shutdown signal that raises KeyboardInterrupt, the bare except swallows it and the worker continues looping. The process cannot be stopped cleanly. Changing the handler to except Exception allows the interrupt to propagate, so the worker exits and a supervisor can restart it.
The same issue appears in scripts that call sys.exit() on a fatal error. A bare except converts an intentional exit into a logged error and continues execution, potentially leaving the program in an inconsistent state.
When a Bare Except Might Be Justified
There are rare cases where catching BaseException is deliberate. A cleanup routine that must run before the interpreter shuts down, such as releasing a lock or flushing a buffer, may need to handle every exception type. Even then, the handler should re-raise after cleanup:
try: work() except BaseException: cleanup() raise
Notice that this uses except BaseException explicitly rather than a bare except. Being explicit makes the intent visible to reviewers and avoids the ambiguity of the bare form. The raise statement without arguments re-raises the original exception, so the process still terminates with the correct traceback.
Writing Handlers That Preserve the Original Failure
A common mistake is to catch an exception and then raise a new one without chaining the original:
try: parse_config(path) except Exception: raise ValueError("configuration failed")
The original traceback is lost unless you use from:
try: parse_config(path) except Exception as exc: raise ValueError("configuration failed") from exc
The from clause sets the __cause__ attribute on the new exception, so the traceback shows both the new error and the original failure. This matters when a bare except has already hidden the first failure; preserving the chain makes the root cause visible during debugging.
Maintainability and Debugging Concerns
A bare except makes code harder to reason about because the reader cannot tell which failures the author intended to handle. When a handler catches KeyboardInterrupt or SystemExit, it changes the control flow of the entire program. Debugging a process that refuses to shut down often leads back to a bare except that swallowed the exit signal.
except Exception still has a broad scope, so in production code it is usually better to catch specific exception types. The choice between bare except and except Exception is the first level of narrowing: it separates process-level control flow from ordinary errors. After that, catching ValueError or KeyError explicitly makes the handler's purpose clear and prevents unrelated failures from being masked.