
Mateen Kiani
Published on Thu Jul 31 2025·4 min read

When you think of exception handling in Python, the try/except duo probably comes to mind. But did you know you can use try without an except clause? This pattern leverages else and finally to separate success and cleanup logic, offering clearer control flow and fewer hidden pitfalls. How can you structure error handling so that success paths and cleanup run smoothly without catching every exception in the same block?
By combining try with else and finally, you can keep error-handling code distinct from normal execution and cleanup logic. The else block runs only when no exception occurs, while finally always runs—perfect for releasing resources. Understanding this pattern helps you write more readable, maintainable code and prevents accidentally swallowing unexpected errors.
Using try alone might seem odd at first, but it offers two main advantages:
except, unexpected errors bubble up, preventing silent failures.try:connection = open_database()except DatabaseError:handle_db_error()else:# Only runs if no exception occurredprocess_data(connection)finally:# Always runsconnection.close()
In this example, we only catch DatabaseError in except (if we had one), run normal logic in else, and always close the connection in finally. If another error happens—say a network glitch—you’ll notice it immediately instead of hiding it.
The else clause runs only when the try block succeeds. Use it to keep your main logic separate from the setup that might fail.
try:file = open("config.json")except OSError:print("Config file missing.")else:data = json.load(file)print("Loaded config:", data)finally:file.close()
This makes it clear:
Tip: Use
elseto guard code that shouldn’t run if setup fails.
Regardless of success or failure, some tasks must always happen: closing files, releasing locks, or ending transactions. Put them in finally.
lock.acquire()try:critical_section()finally:lock.release()
Here, you don’t need an except if you’re not handling errors specifically. Any exception from critical_section() bubbles up, but the lock is always released.
Sometimes you want to detect a condition in else and raise your own exception with a clear message. This can be more informative than letting Python’s default errors surface.
try:value = int(user_input)else:if value < 0:raise ValueError("Negative numbers not allowed")finally:print("Input processing done.")
For more on crafting custom error messages, check out raising exceptions with custom messages in Python.
except if you’re not planning to recover from that error.FileNotFoundError, not a bare except:.try blocks small: Limit the code inside try to only the operations that might fail.else and finally.Avoid using a bare
except:. Let unexpected errors bubble up and be addressed in higher-level logic or logs.
import requeststry:response = requests.get("https://api.example.com/data")response.raise_for_status()else:data = response.json()print(f"Received {len(data)} items.")finally:print("HTTP request complete.")
.raise_for_status() inside the try so HTTP errors become exceptions.else, we assume the request succeeded and parse JSON.finally, we log completion regardless of outcome.Using try without except may seem unconventional, but it clarifies your program’s structure by cleanly separating error handling, successful execution, and cleanup. The else block runs only when no exception occurs, and finally guarantees resource release. When you catch exceptions, focus on those you can handle. Let the rest bubble up and be logged or handled elsewhere.
Next time you find yourself writing a broad try/except just to close a file or release a lock, remember: break it into else and finally. You’ll end up with code that’s safer, easier to read, and less prone to hidden bugs.
