Skip to the content
Software Made Clear Diagrams that show the mechanism About

Python try, except, else and finally: follow one failed conversion

Learn Python try/except with runnable examples. Trace else and finally, catch the right exception, and avoid hiding unrelated failures.

3 min read Last verified September 25, 2026
ANSWER

Put the operation whose failure you can handle in try. A matching except handles its exception; else runs when try finishes normally; finally runs as control leaves the statement. Keep try narrow so unrelated errors do not become misleading recovery messages.

IN PLAIN TERMS

You ask someone to read a number from a form. If the writing is not a number, ask for a correction. If reading succeeds, use the number. Either way, finish the current attempt before starting another one.

Suppose a form contains the text "12", and your program needs an integer. The text "twelve" should produce a useful correction message. A defect in the code that uses the integer should not produce the same message. The placement of try determines whether those two failures stay distinguishable.

Trace success and failure#

This complete example records the execution path so you can inspect it rather than memorise four definitions. The finally event is an observation for the lesson, not a real resource-cleanup operation.

def parse_quantity(raw: str) -> list[str]:
    events: list[str] = []
    try:
        quantity = int(raw)
        events.append("converted")
    except ValueError:
        events.append("enter a whole number")
    else:
        events.append(f"quantity={quantity}")
    finally:
        events.append("attempt finished")
    return events

assert parse_quantity("12") == [
    "converted", "quantity=12", "attempt finished"
]
assert parse_quantity("twelve") == [
    "enter a whole number", "attempt finished"
]
assert parse_quantity("0") == [
    "converted", "quantity=0", "attempt finished"
]

For "twelve", conversion raises ValueError. Python skips the rest of try, runs the matching handler, skips else, and runs finally. For "12", the conversion succeeds and execution reaches else. No partially assigned quantity is read on the error path.

Try it: trace the Python clauses

Assume try calls int(raw), except catches ValueError, else uses the number, and finally cleans up.

The example permits negative quantities because it demonstrates parsing, not a complete order form. Whether negative values are allowed is a separate validation rule. Adding exception handling must not silently decide that business rule.

The Python tutorial explains matching handlers and why an else block can keep successful follow-up work outside the protected operation.

Keep the protected operation small#

Now suppose saving the quantity raises its own ValueError because a configuration setting is invalid. If conversion and saving are both inside the same try, the user could be told to correct a perfectly valid number.

This executable counterexample keeps saving in else, where the conversion handler does not catch its error:

def save_quantity(quantity: int) -> None:
    raise ValueError("storage configuration is invalid")

def submit(raw: str) -> str:
    try:
        quantity = int(raw)
    except ValueError:
        return "enter a whole number"
    else:
        save_quantity(quantity)
        return "saved"

assert submit("twelve") == "enter a whole number"
try:
    submit("12")
except ValueError as error:
    assert str(error) == "storage configuration is invalid"
else:
    raise AssertionError("the storage failure must remain visible")

save_quantity deliberately fails here; it is not a working persistence layer. The point is the different control-flow boundary. Errors raised inside except, else or finally are not handled by the sibling except clauses of that same statement. The language reference defines those rules precisely.

Preserve unexpected errors#

Choose exception types because you have a sensible response to that failure, not because you want the console to stay quiet. except Exception: pass can conceal programming errors and leave the caller believing that work succeeded. A catch-all can be appropriate at an application boundary that logs and reports a failed request, but that is a deliberate failure policy, not successful recovery.

Within a handler, plain raise re-raises the current exception. When converting a low-level error into a more meaningful application error, raise NewError(...) from error preserves the explicit cause. Avoid including secrets or unnecessary personal values in either message.

Some failures need a repaired algorithm rather than another handler. A recursion-depth error caused by a nonterminating call chain will not become correct when you catch it and continue.

Clean up without changing the result#

finally runs while normal Python control leaves the statement, including a pending return or propagated exception. It is not a guarantee against a killed process, machine failure or abrupt interpreter exit. Do not put a new return in finally: that can replace the intended result or suppress a pending exception.

For a file, prefer a context manager instead of manually coordinating open and close. This is a usage fragment; it requires a real quantities.txt file:

with open("quantities.txt", encoding="utf-8") as source:
    first_line = source.readline()

The file is closed when that with block exits normally or via an exception. Reading successfully is still separate from validating the content. The tutorial’s cleanup section explains that resource pattern.

A useful test set covers successful input, the expected conversion failure, and an unexpected downstream failure. The first two tests prove recovery; the third proves you did not hide a different problem. See unit versus integration tests for choosing which boundary each test should exercise.

Questions people also ask

3 QUESTIONS
Can I use try without except in Python?

Yes: try/finally runs cleanup without handling the exception. A try statement cannot stand alone; it needs an except or finally clause. Use with when the resource already supplies an appropriate context manager.

Does except Exception catch everything?

No. It catches Exception and its subclasses, but not all BaseException subclasses, such as KeyboardInterrupt and SystemExit. A bare except catches those too, which is usually not what ordinary application recovery needs.

Does else run after an exception is handled?

No. Else runs only when the try suite completes without an exception and without a return, break or continue. Handling an exception does not turn that execution into the else path.

The library Browse all articles