Skip to main content

Handle KeyboardInterrupt in Python

Error Reference

KeyboardInterrupt

Ctrl+C raised an exception on purpose. Decide whether to clean up, exit quietly, or keep running.

Graceful shutdown

KeyboardInterrupt isn't a bug — it's how Python delivers Ctrl+C so your program gets a chance to exit cleanly instead of dying mid-write:

import sys

def main():
while True:
process_next_item()

if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('\nInterrupted — shutting down cleanly.')
sys.exit(130) # conventional exit code for SIGINT

Cleanup with finally

Resources should be released whether the program finishes or gets interrupted — finally (and with blocks) run in both cases:

conn = open_connection()
try:
run_forever(conn)
except KeyboardInterrupt:
print('Stopping…')
finally:
conn.close() # runs on normal exit AND on Ctrl+C

Why except Exception doesn't catch it

KeyboardInterrupt inherits from BaseException, not Exception — deliberately, so a broad except Exception in a retry loop can't swallow your Ctrl+C and turn the program unkillable:

BaseException
├── SystemExit
├── KeyboardInterrupt <- outside the Exception branch
└── Exception
├── ValueError
├── TypeError
└── ...
while True:
try:
do_work()
except Exception: # errors are retried…
continue
# …but Ctrl+C still stops the loop

This is also the core reason a bare except: is a bug: it catches BaseException and makes the process ignore Ctrl+C.

Long-running services

For servers and workers, translate the signal into an orderly stop instead of an exception mid-task:

import signal

stop = False

def request_stop(signum, frame):
global stop
stop = True

signal.signal(signal.SIGINT, request_stop)

while not stop:
process_one_batch() # finishes the current batch, then exits
print('Drained cleanly.')

Frequently Asked Questions

Should I catch KeyboardInterrupt at all?

Catch it once, at the top level, to clean up and exit — closing files, committing or rolling back transactions, printing a friendly message. Avoid catching it deep inside loops or libraries, where suppressing it makes the program hard to stop.

Why does my program ignore Ctrl+C?

Usually a bare except: or except BaseException: somewhere is swallowing the KeyboardInterrupt. Blocking C extensions or threads can also delay delivery — in threads, only the main thread receives KeyboardInterrupt.

try:
risky()
except Exception: # not bare 'except:' — Ctrl+C passes through
handle()

What exit code should a Ctrl+C exit use?

Convention on Unix is 130 (128 + SIGINT signal number 2). Shells and process supervisors use it to distinguish "user interrupted" from success (0) or failure (1).

Next up in your learning path