SystemExit
sys.exit() is just raise SystemExit(code): finally blocks run, it can be caught, and the code decides the exit status — None → 0, int → itself, anything else → printed, status 1.
Demo
import sys try: sys.exit(3) except SystemExit as e: r = e.code r
e.code is stored as-is — 3 stays an int, a message stays a string, and SystemExit() with no argument has code None. The interpreter converts it to a process status only at the very end: None → 0, int → that number, anything else is printed to stderr and becomes 1. The demo catches it on purpose; uncaught, the program would simply end without a traceback.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| code | int | str | None | no (None) | The exit status or message, stored as e.code. None (or no argument) → status 0; an int → that status; any other object → printed to stderr, status 1. With several arguments, code is the whole args tuple. |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| code | object | The exit status or message passed to the constructor / sys.exit(). None by default; the args tuple when there are several arguments. |
| args | tuple | Constructor arguments. sys.exit() with no argument gives an empty tuple. |
Common patterns
import sys def main() -> int: if not check(): print('check failed', file=sys.stderr) return 1 return 0 if __name__ == '__main__': sys.exit(main())
if not os.path.exists(path): sys.exit(f'error: {path} not found')
try: cli(['--bad-flag']) except SystemExit as e: assert e.code == 2
Examples
Pitfalls
import subprocess, sys cmd = [sys.executable, '-c', "import sys; sys.exit('0')"] r = subprocess.run(cmd, capture_output=True, text=True) (r.returncode, r.stderr)
import subprocess, sys cmd = [sys.executable, '-c', "import sys; sys.exit(0)"] r = subprocess.run(cmd, capture_output=True, text=True) (r.returncode, r.stderr)
import argparse p = argparse.ArgumentParser(prog='tool') p.add_argument('--n', type=int) try: try: p.parse_args(['--n', 'x']) except Exception: r = 'handled' except SystemExit as e: r = f'exited with {e.code}' r
import argparse p = argparse.ArgumentParser(prog='tool', exit_on_error=False) p.add_argument('--n', type=int) try: p.parse_args(['--n', 'x']) except argparse.ArgumentError as e: r = str(e) r
import sys log = [] try: sys.exit(1) except: log.append('error ignored') log.append('still running') log
import sys log = [] try: try: sys.exit(1) except Exception: log.append('error ignored') log.append('still running') except SystemExit: log.append('exited') log
When to use
- Ending a script with a status code other programs can check
- Failing fast with a message: sys.exit("error: ...")
- Catching it in tests or in a host that runs user scripts
- Signalling an error inside a library → raise a real exception; let the caller decide to exit
- Immediate exit that skips cleanup (after fork) → os._exit()
- exit() / quit() in scripts → they are REPL helpers added by site; use sys.exit()
Notes
FAQ
sys.exit() and sys.exit(None) exit with 0. sys.exit(n) with an int exits with n. Any other value — sys.exit('message') — is printed to stderr and the status is 1. True counts as the int 1. A string that looks like a number is still a string: sys.exit('0') exits with 1.