Skip to content

How to Catch KeyboardInterrupt in Python

Learn how to handle Ctrl+C interruptions in Python with try/except blocks to perform cleanup, save progress, and exit gracefully in long-running scripts.

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:
    pass
except KeyboardInterrupt:
    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:
        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.

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()

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

Pattern Use 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.

Navigation