Why is "except: pass" a bad programming practice?
Asked Answered
H

19

418

I often see comments on other Stack Overflow questions about how the use of except: pass is discouraged. Why is this bad? Sometimes I just don't care what the errors are and I want to just continue with the code.

try:
    something
except:
    pass

Why is using an except: pass block bad? What makes it bad? Is it the fact that I pass on an error or that I except any error?

Herbarium answered 4/2, 2014 at 13:2 Comment(4)
It the very least, I would suggest you log it, so you KNOW what problems you're ignoring. Use the logging module at DEBUG level to avoid them streaming out in production, but keep them available in development.Selfexcited
Related: What is wrong with using a bare 'except'?Catenoid
Related: Why use contextlib.suppress as opposed to try/except with pass?Lafayette
See also Bad idea to catch all exceptions in PythonOrlandoorlanta
C
457

As you correctly guessed, there are two sides to it: Catching any error by specifying no exception type after except, and simply passing it without taking any action.

My explanation is “a bit” longer—so tl;dr it breaks down to this:

  1. Don’t catch any error. Always specify which exceptions you are prepared to recover from and only catch those.
  2. Try to avoid passing in except blocks. Unless explicitly desired, this is usually not a good sign.

But let’s go into detail:

Don’t catch any error

When using a try block, you usually do this because you know that there is a chance of an exception being thrown. As such, you also already have an approximate idea of what can break and what exception can be thrown. In such cases, you catch an exception because you can positively recover from it. That means that you are prepared for the exception and have some alternative plan which you will follow in case of that exception.

For example, when you ask for the user to input a number, you can convert the input using int() which might raise a ValueError. You can easily recover that by simply asking the user to try it again, so catching the ValueError and prompting the user again would be an appropriate plan. A different example would be if you want to read some configuration from a file, and that file happens to not exist. Because it is a configuration file, you might have some default configuration as a fallback, so the file is not exactly necessary. So catching a FileNotFoundError and simply applying the default configuration would be a good plan here. Now in both these cases, we have a very specific exception we expect and have an equally specific plan to recover from it. As such, in each case, we explicitly only except that certain exception.

However, if we were to catch everything, then—in addition to those exceptions we are prepared to recover from—there is also a chance that we get exceptions that we didn’t expect, and which we indeed cannot recover from; or shouldn’t recover from.

Let’s take the configuration file example from above. In case of a missing file, we just applied our default configuration and might decide at a later point to automatically save the configuration (so next time, the file exists). Now imagine we get a IsADirectoryError, or a PermissionError instead. In such cases, we probably do not want to continue; we could still apply our default configuration, but we later won’t be able to save the file. And it’s likely that the user meant to have a custom configuration too, so using the default values is likely not desired. So we would want to tell the user about it immediately, and probably abort the program execution too. But that’s not something we want to do somewhere deep within some small code part; this is something of application-level importance, so it should be handled at the top—so let the exception bubble up.

Another simple example is also mentioned in the Python 2 idioms document. Here, a simple typo exists in the code which causes it to break. Because we are catching every exception, we also catch NameErrors and SyntaxErrors. Both are mistakes that happen to us all while programming and both are mistakes we absolutely don’t want to include when shipping the code. But because we also caught those, we won’t even know that they occurred there and lose any help to debug it correctly.

But there are also more dangerous exceptions which we are unlikely prepared for. For example, SystemError is usually something that happens rarely and which we cannot really plan for; it means there is something more complicated going on, something that likely prevents us from continuing the current task.

In any case, it’s very unlikely that you are prepared for everything in a small-scale part of the code, so that’s really where you should only catch those exceptions you are prepared for. Some people suggest to at least catch Exception as it won’t include things like SystemExit and KeyboardInterrupt which by design are to terminate your application, but I would argue that this is still far too unspecific. There is only one place where I personally accept catching Exception or just any exception, and that is in a single global application-level exception handler which has the single purpose to log any exception we were not prepared for. That way, we can still retain as much information about unexpected exceptions, which we then can use to extend our code to handle those explicitly (if we can recover from them) or—in case of a bug—to create test cases to make sure it won’t happen again. But of course, that only works if we only ever caught those exceptions we were already expecting, so the ones we didn’t expect will naturally bubble up.

Try to avoid passing in except blocks

When explicitly catching a small selection of specific exceptions, there are many situations in which we will be fine by simply doing nothing. In such cases, just having except SomeSpecificException: pass is just fine. Most of the time though, this is not the case as we likely need some code related to the recovery process (as mentioned above). This can be for example something that retries the action again, or to set up a default value instead.

If that’s not the case though, for example, because our code is already structured to repeat until it succeeds, then just passing is good enough. Taking our example from above, we might want to ask the user to enter a number. Because we know that users like to not do what we ask them for, we might just put it into a loop in the first place, so it could look like this:

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

Because we keep trying until no exception is thrown, we don’t need to do anything special in the except block, so this is fine. But of course, one might argue that we at least want to show the user some error message to tell him why he has to repeat the input.

In many other cases though, just passing in an except is a sign that we weren’t really prepared for the exception we are catching. Unless those exceptions are simple (like ValueError or TypeError), and the reason why we can pass is obvious, try to avoid just passing. If there’s really nothing to do (and you are absolutely sure about it), then consider adding a comment why that’s the case; otherwise, expand the except block to actually include some recovery code.

except: pass

The worst offender though is the combination of both. This means that we are willingly catching any error although we are absolutely not prepared for it and we also don’t do anything about it. You at least want to log the error and also likely reraise it to still terminate the application (it’s unlikely you can continue like normal after a MemoryError). Just passing though will not only keep the application somewhat alive (depending on where you catch of course), but also throw away all the information, making it impossible to discover the error—which is especially true if you are not the one discovering it.


So the bottom line is: Catch only exceptions you really expect and are prepared to recover from; all others are likely either mistakes you should fix or something you are not prepared for anyway. Passing specific exceptions are fine if you really don’t need to do something about them. In all other cases, it’s just a sign of presumption and being lazy. And you definitely want to fix that.

Comptroller answered 4/2, 2014 at 13:27 Comment(6)
"You at least want to log the error and also likely reraise it to still terminate the application". Can you demonstrate how to "reraise" an exception to let it continue bubbling up even after catching it? This seems useful to me to add in some custom error messages while still letting the exception force the application to quit.Showers
This helps clarify: they use the blanket except, but then call raise with no arguments to continue letting the exception bubble up, terminating the application. I like it: ianbicking.org/blog/2007/09/re-raising-exceptions.html. Looks like a solid exception to the rule about not using the blanket except.Showers
@GabrielStaples Yeah, a caught exception can be rethrown using raise. You usually would only do this in few locations within your application to log the exception though.Comptroller
This is great, avoid passing in except blocks. I would say that do whatever seems more understandable, especially to others. Get a second set of python eyes to review your code and see if they question the block. Readability is key.Harrington
@Comptroller You may use raise Exception() from eSubedit
Another example: you parse some extra numbers from integrated payment service and the "catch" defaults them to "0". As a result after few months you realized you were billing your customers wrong all that time. :-) It ends up in a court and your company pays a big sum for compensation (you are fired).Geodynamics
C
281

The main problem here is that it ignores all and any error: Out of memory, CPU is burning, user wants to stop, program wants to exit, Jabberwocky is killing users.

This is way too much. In your head, you're thinking "I want to ignore this network error". If something unexpected goes wrong, then your code silently continues and breaks in completely unpredictable ways that no one can debug.

That's why you should limit yourself to ignoring specifically only some errors and let the rest pass.

Ceratoid answered 4/2, 2014 at 13:12 Comment(0)
I
92

Executing your pseudo code literally does not even give any error:

try:
    something
except:
    pass

as if it is a perfectly valid piece of code, instead of throwing a NameError. I hope this is not what you want.

Illyes answered 4/2, 2014 at 13:7 Comment(0)
S
61

Why is “except: pass” a bad programming practice?

Why is this bad?

try:
    something
except:
    pass

This catches every possible exception, including GeneratorExit, KeyboardInterrupt, and SystemExit - which are exceptions you probably don't intend to catch. It's the same as catching BaseException.

try:
    something
except BaseException:
    pass

Older versions of the documentation say:

Since every error in Python raises an exception, using except: can make many programming errors look like runtime problems, which hinders the debugging process.

Python Exception Hierarchy

If you catch a parent exception class, you also catch all of their child classes. It is much more elegant to only catch the exceptions you are prepared to handle.

Here's the Python 3 exception hierarchy - do you really want to catch 'em all?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

Don't Do this

If you're using this form of exception handling:

try:
    something
except: # don't just do a bare except!
    pass

Then you won't be able to interrupt your something block with Ctrl-C. Your program will overlook every possible Exception inside the try code block.

Here's another example that will have the same undesirable behavior:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

Instead, try to only catch the specific exception you know you're looking for. For example, if you know you might get a value-error on a conversion:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

Further Explanation with another example

You might be doing it because you've been web-scraping and been getting say, a UnicodeError, but because you've used the broadest Exception catching, your code, which may have other fundamental flaws, will attempt to run to completion, wasting bandwidth, processing time, wear and tear on your equipment, running out of memory, collecting garbage data, etc.

If other people are asking you to complete so that they can rely on your code, I understand feeling compelled to just handle everything. But if you're willing to fail noisily as you develop, you will have the opportunity to correct problems that might only pop up intermittently, but that would be long term costly bugs.

With more precise error handling, you code can be more robust.

Shelleyshellfire answered 4/2, 2014 at 20:52 Comment(0)
S
31
>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

So, here is my opinion. Whenever you find an error, you should do something to handle it, i.e. write it in logfile or something else. At least, it informs you that there used to be a error.

Spoonfeed answered 4/2, 2014 at 13:10 Comment(3)
-1 Argument from authority doesn't actually explain anything. Authority can be wrong.Blockhead
what @Blockhead wrote, AND, one line below it, the same authority writes: "Unless explicitly silenced", which is exactly what except: pass does.Gazehound
@OfriRaviv No, isn't the error passing implicitly? Explicitly would require naming the error that ought to pass silently, that is, being explicit about it. This is not what except:pass does.Exploitation
T
27

You should use at least except Exception: to avoid catching system exceptions like SystemExit or KeyboardInterrupt. Here's link to docs.

In general you should define explicitly exceptions you want to catch, to avoid catching unwanted exceptions. You should know what exceptions you ignore.

Tallu answered 4/2, 2014 at 13:7 Comment(0)
F
16

First, it violates two principles of Zen of Python:

  • Explicit is better than implicit
  • Errors should never pass silently

What it means, is that you intentionally make your error pass silently. Moreover, you don't event know, which error exactly occurred, because except: pass will catch any exception.

Second, if we try to abstract away from the Zen of Python, and speak in term of just sanity, you should know, that using except:pass leaves you with no knowledge and control in your system. The rule of thumb is to raise an exception, if error happens, and take appropriate actions. If you don't know in advance, what actions these should be, at least log the error somewhere (and better re-raise the exception):

try:
    something
except:
    logger.exception('Something happened')

But, usually, if you try to catch any exception, you are probably doing something wrong!

Firstly answered 4/2, 2014 at 13:8 Comment(4)
...Unless explicitly silenced, which is the case with OP.Ecstatics
I want to know your solution. In fact, when really nothing needs doing, I just list the errors in except and make comments and write logs. Then just pass.Spoonfeed
@Hyperboreus, I don't think, that catching all and any errors is explicitly silencing them, i.e, you don't even know, what do u catch.Firstly
"Because some guy says so" is not really an answer to a "Why?" question.Greenbrier
E
14

The #1 reason has already been stated - it hides errors that you did not expect.

(#2) - It makes your code difficult for others to read and understand. If you catch a FileNotFoundException when you are trying to read a file, then it is pretty obvious to another developer what functionality the 'catch' block should have. If you do not specify an exception, then you need additional commenting to explain what the block should do.

(#3) - It demonstrates lazy programming. If you use the generic try/catch, it indicates either that you do not understand the possible run-time errors in your program, or that you do not know what exceptions are possible in Python. Catching a specific error shows that you understand both your program and the range of errors that Python throws. This is more likely to make other developers and code-reviewers trust your work.

Ealing answered 5/2, 2014 at 1:34 Comment(0)
C
14

So, what output does this code produce?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

Now imagine the try-except block is hundreds of lines of calls to a complex object hierarchy, and is itself called in the middle of large program's call tree. When the program goes wrong, where do you start looking?

Clicker answered 5/2, 2014 at 15:41 Comment(2)
Er, thanks to folks who 'corrected' this, but please don't - it's intentionally wrong, in the 'interview question' sense. It's possibly more subtle that it first appears - try it. My point is that squashing 'all' exceptions, especially in Python, makes debugging difficult, even in a trivial dozen lines of code.Clicker
Tried it. This is a brilliant example. I could not spot the errors without the stacktrace.Corallite
P
12

The except:pass construct essentially silences any and all exceptional conditions that come up while the code covered in the try: block is being run.

What makes this bad practice is that it usually isn't what you really want. More often, some specific condition is coming up that you want to silence, and except:pass is too much of a blunt instrument. It will get the job done, but it will also mask other error conditions that you likely haven't anticipated, but may very well want to deal with in some other way.

What makes this particularly important in Python is that by the idioms of this language, exceptions are not necessarily errors. They're often used this way, of course, just as in most languages. But Python in particular has occasionally used them to implement an alternative exit path from some code tasks which isn't really part of the normal running case, but is still known to come up from time to time and may even be expected in most cases. SystemExit has already been mentioned as an old example, but the most common example nowadays may be StopIteration. Using exceptions this way caused a lot of controversy, especially when iterators and generators were first introduced to Python, but eventually the idea prevailed.

Putupon answered 4/2, 2014 at 15:10 Comment(0)
H
11

In general, you can classify any error/exception in one of three categories:

  • Fatal: Not your fault, you cannot prevent them, you cannot recover from them. You should certainly not ignore them and continue, and leave your program in an unknown state. Just let the error terminate your program, there is nothing you can do.

  • Boneheaded: Your own fault, most likely due to an oversight, bug or programming error. You should fix the bug. Again, you should most certainly not ignore and continue.

  • Exogenous: You can expect these errors in exceptional situations, such as file not found or connection terminated. You should explicitly handle these errors, and only these.

In all cases except: pass will only leave your program in an unknown state, where it can cause more damage.

Hemipterous answered 5/2, 2014 at 17:1 Comment(0)
P
6

All comments brought up so far are valid. Where possible you need to specify what exactly exception you want to ignore. Where possible you need to analyze what caused exception, and only ignore what you meant to ignore, and not the rest. If exception causes application to "crash spectacularly", then be it, because it's much more important to know the unexpected happened when it happened, than concealing that the problem ever occurred.

With all that said, do not take any programming practice as a paramount. This is stupid. There always is the time and place to do ignore-all-exceptions block.

Another example of idiotic paramount is usage of goto operator. When I was in school, our professor taught us goto operator just to mention that thou shalt not use it, EVER. Don't believe people telling you that xyz should never be used and there cannot be a scenario when it is useful. There always is.

Pantisocracy answered 4/2, 2014 at 20:52 Comment(4)
The "goto" case is stylistic and a matter of opinion, whereas "except: pass" is usually factually wrong. It assumes that if someone were to, for example, "kill -TERM" your process at that point then it should ignore it. At the very least that's bad behaviour.Crawley
@Crawley yet there are cases where this is appropriate to use. For example when a function you are calling is supplemental, of unknown origin/author, does not affect core functionality, but if crashes could cause trouble. I do realize you would argue that such calls should be properly researched and analyzed, but in real life it is not always possible.Pantisocracy
Still, if I want to terminate your process, kill -9 should not be the only reliable option.Crawley
There is another side to the except: pass evaluation, which is, how important it is that the line of code under try succeeds, vs how important it is that the overall code flow succeeds.Stranger
B
6

In my opinion errors have a reason to appear, that my sound stupid, but thats the way it is. Good programming only raises errors when you have to handle them. Also, as i read some time ago, "the pass-Statement is a Statement that Shows code will be inserted later", so if you want to have an empty except-statement feel free to do so, but for a good program there will be a part missing. because you dont handle the things you should have. Appearing exceptions give you the chance to correct input data or to change your data structure so these exceptions dont occur again (but in most cases (Network-exceptions, General input-exceptions) exceptions indicate that the next parts of the program wont execute well. For example a NetworkException can indicate a broken network-connection and the program cant send/recieve data in the next program steps.

But using a pass block for only one execption-block is valid, because you still differenciate beetween the types of exceptions, so if you put all exception-blocks in one, it is not empty:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

can be rewritten that way:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

So even multiple except-blocks with pass-statements can result in code, whose structure handles special types of exceptions.

Blen answered 4/2, 2014 at 21:25 Comment(0)
S
6

Simply put, if an exception or error is thrown, something's wrong. It may not be something very wrong, but creating, throwing, and catching errors and exceptions just for the sake of using goto statements is not a good idea, and it's rarely done. 99% of the time, there was a problem somewhere.

Problems need to be dealt with. Just like how it is in life, in programming, if you just leave problems alone and try to ignore them, they don't just go away on their own a lot of times; instead they get bigger and multiply. To prevent a problem from growing on you and striking again further down the road, you either 1) eliminate it and clean up the mess afterwards, or 2) contain it and clean up the mess afterwards.

Just ignoring exceptions and errors and leaving them be like that is a good way to experience memory leaks, outstanding database connections, needless locks on file permissions, etc.

On rare occasions, the problem is so miniscule, trivial, and - aside from needing a try...catch block - self-contained, that there really is just no mess to be cleaned up afterwards. These are the only occasions when this best practice doesn't necessarily apply. In my experience, this has generally meant that whatever the code is doing is basically petty and forgoable, and something like retry attempts or special messages are worth neither the complexity nor holding the thread up on.

At my company, the rule is to almost always do something in a catch block, and if you don't do anything, then you must always place a comment with a very good reason why not. You must never pass or leave an empty catch block when there is anything to be done.

Secondary answered 4/2, 2014 at 23:20 Comment(0)
I
3

Since it hasn't been mentioned yet, it's better style to use contextlib.suppress:

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

In this example, somefile.tmp will be non-existent after this block of code executes without raising any exceptions (other than FileNotFoundError, which is suppressed).

Interruption answered 4/12, 2018 at 20:1 Comment(0)
M
2

​Handling errors is very important in programming. You do need to show the user what went wrong. In very few cases you can ignore the errors. This is it is very bad programming practice.

Mossberg answered 8/2, 2014 at 0:47 Comment(0)
C
2

if it was bad practice "pass" would not be an option. if you have an asset that receives information from many places IE a form or userInput it comes in handy.

variable = False
try:
    if request.form['variable'] == '1':
       variable = True
except:
    pass
Cumming answered 31/12, 2020 at 21:41 Comment(0)
C
2

I personally prefer this solution:

except ValueError as error:
                print(error.args)
                pass

error.args gives me a one-liner that is not too distracting but really helps with code review, especially if there are different reasons for the errors such as

(ValueError('year 0 is out of range'),)
(ValueError('month must be in 1..12'),)
(ValueError('day is out of range for month'),)

when working with time periods in pandas.

Capp answered 9/3, 2022 at 21:0 Comment(0)
C
1

I am building an application that will run in a data center. It should not generate any errors or raise any exceptions. My data center has a network monitoring system, which includes an SNMP trap receiver.

try:
    main()
except as e:
    log(str(e))
    send_snmp_trap(str(e))
    raise

except that raise isn't going to go anywhere because it's and the bottom of any stack that might be left.

BTW, this is not a universal panacea by any means. There are some exceptions that can't be caught. SNMP doesn't guarantee delivery. YMMV.

Coelenterate answered 16/12, 2020 at 3:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.