Skip to main content

How to Catch KeyboardInterrupt in Python

KeyboardInterrupt is a built-in Python exception that's raised when a user presses Ctrl+C to stop a running program. By default, this immediately terminates the program. However, catching this exception lets you perform cleanup operations, save progress, close connections, or display a friendly exit message before shutting down.

This is especially important in programs that involve long-running processes, file operations, network connections, or database transactions where abrupt termination could cause data loss or corruption.

Basic Syntax​

Catch KeyboardInterrupt using a standard try/except block:

try:
# Code that might be interrupted
pass
except KeyboardInterrupt:
# Handle the interruption gracefully
print("Program interrupted by user.")

Simple Example: User Input Loop​

A common scenario is a program that continuously accepts user input and needs a clean way to exit:

try:
while True:
user_input = input("Enter something (Ctrl+C to exit): ")
print(f"You entered: {user_input}")
except KeyboardInterrupt:
print("\nšŸ‘‹ Program terminated by user. Goodbye!")

Output (after pressing Ctrl+C):

Enter something (Ctrl+C to exit): hello
You entered: hello
Enter something (Ctrl+C to exit): ^C
šŸ‘‹ Program terminated by user. Goodbye!

Without the except KeyboardInterrupt block, pressing Ctrl+C would display an ugly traceback instead of a clean message.

Handling Long-Running Processes​

For processes that take time to complete, catching KeyboardInterrupt lets you stop gracefully and perform cleanup:

import time

def process_data():
total_steps = 10

try:
print(f"Processing {total_steps} steps. Press Ctrl+C to cancel.\n")
for i in range(1, total_steps + 1):
print(f"Step {i}/{total_steps}: processing...")
time.sleep(1) # Simulate work
print(f"Step {i}/{total_steps}: complete āœ…")
print("\nšŸŽ‰ All steps completed successfully!")

except KeyboardInterrupt:
print(f"\n\nāš ļø Interrupted at step {i}/{total_steps}")
print("Partial results may be available.")

process_data()

Output (pressing Ctrl+C during step 3):

Processing 10 steps. Press Ctrl+C to cancel.

Step 1/10: processing...
Step 1/10: complete āœ…
Step 2/10: processing...
Step 2/10: complete āœ…
Step 3/10: processing...
^C

āš ļø Interrupted at step 3/10
Partial results may be available.

Using finally for Guaranteed Cleanup​

The finally block runs regardless of whether an exception occurred, whether the program completes normally, is interrupted, or encounters an error:

import time

def write_report():
data = []

try:
print("Collecting data... Press Ctrl+C to stop early.\n")
for i in range(1, 11):
data.append(f"Record {i}")
print(f"Collected record {i}")
time.sleep(0.5)

except KeyboardInterrupt:
print(f"\nāš ļø Collection interrupted after {len(data)} records.")

finally:
# This ALWAYS runs: even after KeyboardInterrupt
print(f"\nšŸ“„ Saving {len(data)} records to report.txt...")
with open("report.txt", "w") as f:
f.write("\n".join(data))
print("āœ… Report saved successfully.")

write_report()

Output (pressing Ctrl+C after 4 records):

Collecting data... Press Ctrl+C to stop early.

Collected record 1
Collected record 2
Collected record 3
Collected record 4
^C
āš ļø Collection interrupted after 4 records.

šŸ“„ Saving 4 records to report.txt...
āœ… Report saved successfully.
Always use finally for critical cleanup

The finally block is the safest place for operations that must happen: closing files, releasing locks, saving state, or disconnecting from databases. It runs even if sys.exit() is called inside the except block.

Catching KeyboardInterrupt with Other Exceptions​

You can handle KeyboardInterrupt alongside other exceptions, but keep them in separate except blocks to handle each appropriately:

import time

def divide_numbers():
try:
while True:
num = input("Enter a number (Ctrl+C to quit): ")
result = 100 / int(num)
print(f"100 / {num} = {result:.2f}\n")

except KeyboardInterrupt:
print("\nšŸ‘‹ Exiting calculator.")

except ValueError:
print("āŒ Please enter a valid integer.")

except ZeroDivisionError:
print("āŒ Cannot divide by zero.")

divide_numbers()
Don't catch KeyboardInterrupt with a bare except

A bare except: (without specifying the exception type) catches everything, including KeyboardInterrupt and SystemExit, which can make your program impossible to stop:

# BAD: Catches KeyboardInterrupt, making Ctrl+C useless
try:
while True:
pass
except: # Catches EVERYTHING: avoid this!
pass

# GOOD: Only catch specific exceptions
try:
while True:
pass
except Exception: # Doesn't catch KeyboardInterrupt or SystemExit
pass

Use except Exception: instead of bare except: to let KeyboardInterrupt and SystemExit propagate normally.

Nested Exception Handling​

For complex programs, you might want different handlers at different levels:

import time

def process_item(item):
"""Process a single item: may raise various errors."""
time.sleep(0.3)
if item == 5:
raise ValueError(f"Invalid item: {item}")
print(f" Processed item {item} āœ…")

def main():
items = range(1, 11)

try:
print("Starting batch processing...\n")
for item in items:
try:
process_item(item)
except ValueError as e:
print(f" āš ļø Skipping: {e}")
continue

print("\nšŸŽ‰ Batch processing complete!")

except KeyboardInterrupt:
print(f"\n\nāŒ Batch interrupted at item {item}.")
print(f" {item - 1} items were processed successfully.")

main()

Output (pressing Ctrl+C during item 7):

Starting batch processing...

Processed item 1 āœ…
Processed item 2 āœ…
Processed item 3 āœ…
Processed item 4 āœ…
āš ļø Skipping: Invalid item: 5
Processed item 6 āœ…
^C

āŒ Batch interrupted at item 7.
6 items were processed successfully.

Practical Example: Database Connection Handler​

import time

class DatabaseConnection:
def __init__(self, name):
self.name = name
self.connected = False

def connect(self):
print(f"šŸ“” Connected to {self.name}")
self.connected = True

def close(self):
if self.connected:
print(f"šŸ”’ Connection to {self.name} closed")
self.connected = False

def run_queries():
db = DatabaseConnection("production_db")

try:
db.connect()
print("Running queries... Press Ctrl+C to abort.\n")

for i in range(1, 101):
print(f"Query {i}: executing...")
time.sleep(0.5)

except KeyboardInterrupt:
print("\n\nāš ļø Query execution interrupted!")

finally:
db.close()
print("āœ… Cleanup complete.")

run_queries()

Output (pressing Ctrl+C):

šŸ“” Connected to production_db
Running queries... Press Ctrl+C to abort.

Query 1: executing...
Query 2: executing...
Query 3: executing...
^C

āš ļø Query execution interrupted!
šŸ”’ Connection to production_db closed
āœ… Cleanup complete.

Quick Reference​

PatternUse Case
except KeyboardInterrupt:Catch Ctrl+C and handle gracefully
finally:Guaranteed cleanup (files, connections)
except Exception:Catch errors but let KeyboardInterrupt through
except (KeyboardInterrupt, SystemExit):Handle both user and system exits

Conclusion​

Catching KeyboardInterrupt in Python is essential for building robust, user-friendly programs. Use a try/except KeyboardInterrupt block to display clean exit messages, and add a finally block when you need guaranteed cleanup, i.e. saving data, closing files, or releasing resources. Avoid bare except: clauses that accidentally trap keyboard interrupts, and keep your exception handlers specific to handle each error type appropriately. With proper interrupt handling, your programs exit gracefully regardless of how they're stopped.