os._exit

sys.exit raises SystemExit and lets Python clean up; os._exit ends the process on the spot. Use it in a forked child, almost nowhere else. os._exit and os.EX_OK exist on Unix and Windows; the other EX_* codes are Unix only.

os functionPython 3 (all versions)
Common call
os._exit(0)
Returns
never returns
Replaces
sys.exit(n) is the normal way out
Watch out
unflushed print() output is lost
os._exit(n) / os.EX_OK, os.EX_USAGE, os.EX_DATAERR, ... (sysexits exit codes)
→ never returns

Parameters

NameTypeRequiredDescription
nintyesThe exit status. Unix keeps only the low 8 bits (0-255); 0 (EX_OK) means success.

Return value

never returns — The process ends with exit status n. The EX_* names are plain int constants.

Common patterns

End a forked child (Unix)
Flush, then _exit, so the child never runs the parent code or its atexit handlers.
import os, sys
pid = os.fork()
if pid == 0:
    try:
        do_child_work()
        code = os.EX_OK
    except Exception:
        code = os.EX_SOFTWARE
    finally:
        sys.stdout.flush()
        os._exit(code)
Command-line tool with sysexits codes
EX_USAGE for bad arguments, EX_NOINPUT for a missing input file. getattr keeps it working on Windows.
import os, sys
EX_USAGE = getattr(os, 'EX_USAGE', 64)
EX_NOINPUT = getattr(os, 'EX_NOINPUT', 66)
if len(sys.argv) != 2:
    print('usage: tool FILE', file=sys.stderr)
    sys.exit(EX_USAGE)
if not os.path.exists(sys.argv[1]):
    sys.exit(EX_NOINPUT)
Hard stop from a watchdog thread
sys.exit in a thread only ends that thread; os._exit ends the whole process.
import os, sys, threading
def watchdog():
    if not finished.wait(timeout=60):
        print('timed out', file=sys.stderr, flush=True)
        os._exit(1)
finished = threading.Event()
threading.Thread(target=watchdog, daemon=True).start()

Examples

1. EX_OK is 0
import os os.EX_OK
Returns
0
2. _exit sets the exit code
import subprocess, sys r = subprocess.run([sys.executable, '-c', 'import os; os._exit(5)']) r.returncode
Returns
5
3. finally blocks are skipped
import subprocess, sys code = 'import os\ntry:\n os._exit(3)\nfinally:\n print("finally ran")' r = subprocess.run([sys.executable, '-c', code], capture_output=True, text=True) (r.returncode, r.stdout)
Returns
(3, '')
4. sys.exit runs them
import subprocess, sys code = 'import sys\ntry:\n sys.exit(3)\nfinally:\n print("finally ran")' r = subprocess.run([sys.executable, '-c', code], capture_output=True, text=True) (r.returncode, r.stdout)
Returns
(3, 'finally ran\n')
5. sys.exit is just an exception
import sys try: sys.exit(3) except SystemExit as e: result = ('caught', e.code) result
Returns
('caught', 3)
6. sys.exit with a message: stderr and code 1
import subprocess, sys r = subprocess.run([sys.executable, '-c', 'import sys; sys.exit("bye")'], capture_output=True, text=True) (r.returncode, r.stderr)
Returns
(1, 'bye\n')
7. EX_* with a fallback for Windows
import os getattr(os, 'EX_USAGE', 64)
Returns
64

Pitfalls

1. Expecting cleanup after os._exit
atexit handlers, finally blocks and object finalizers do not run. If cleanup matters, use sys.exit - or do the cleanup yourself before calling _exit.
os._exit
import subprocess, sys
code = 'import atexit, os\natexit.register(print, "cleanup ran")\nos._exit(0)'
subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
''
sys.exit
import subprocess, sys
code = 'import atexit, sys\natexit.register(print, "cleanup ran")\nsys.exit(0)'
subprocess.run([sys.executable, '-c', code], capture_output=True, text=True).stdout
'cleanup ran\n'
2. Losing printed output
stdout to a pipe or file is block-buffered and _exit does not flush it. Flush (print(..., flush=True) or sys.stdout.flush()) before _exit.
no flush
import subprocess, sys
code = 'import os; print("saved?", end=""); os._exit(0)'
r = subprocess.run([sys.executable, '-c', code], capture_output=True, text=True)
r.stdout
''
flush=True
import subprocess, sys
code = 'import os; print("saved", end="", flush=True); os._exit(0)'
r = subprocess.run([sys.executable, '-c', code], capture_output=True, text=True)
r.stdout
'saved'

When to use

Use it
  • The child process after os.fork() - the docs name this as the normal use
  • Ending the whole process from a thread or after a fatal state where cleanup could hang
  • EX_* codes: Unix command-line tools that want conventional, documented exit codes
Reach for something else
  • Normal program exit → sys.exit(n) (or just return from main)
  • Any exit where files, buffers or atexit handlers must be flushed
  • EX_* names other than EX_OK in code that must run on Windows - they do not exist there

Notes

CPython impl
os._exit calls the C _exit() directly (Modules/posixmodule.c). sys.exit raises SystemExit, which unwinds the stack, then interpreter shutdown runs atexit handlers and flushes stdio.
Availability
_exit: everywhere. EX_OK: Unix, Windows. EX_USAGE ... EX_CONFIG: Unix only (not WASI), and only where the platform defines them.
Values on Linux
EX_OK 0, EX_USAGE 64, EX_DATAERR 65, EX_NOINPUT 66, EX_NOUSER 67, EX_NOHOST 68, EX_UNAVAILABLE 69, EX_SOFTWARE 70, EX_OSERR 71, EX_OSFILE 72, EX_CANTCREAT 73, EX_IOERR 74, EX_TEMPFAIL 75, EX_PROTOCOL 76, EX_NOPERM 77, EX_CONFIG 78.
Range
Unix keeps only the low 8 bits: in our test os._exit(256) gave exit code 0 and os._exit(-1) gave 255 on Linux, but 256 and 4294967295 on Windows. Stay within 0-255.
Meanings
EX_USAGE wrong usage/arguments, EX_DATAERR bad input data, EX_NOINPUT input file missing or unreadable, EX_NOUSER / EX_NOHOST unknown user / host, EX_UNAVAILABLE service unavailable, EX_SOFTWARE internal error, EX_OSERR OS error (e.g. cannot fork), EX_OSFILE system file problem, EX_CANTCREAT cannot create output file, EX_IOERR I/O error, EX_TEMPFAIL temporary failure (retry later), EX_PROTOCOL protocol error, EX_NOPERM insufficient permission, EX_CONFIG configuration error.

FAQ

os._exit would end the page process itself, so the examples call it only in a separate Python child. os._exit and os.EX_OK work on Unix and Windows; EX_USAGE, EX_DATAERR and the other EX_* codes are Unix only (not WASI), so on Windows os.EX_USAGE raises AttributeError - use getattr(os, "EX_USAGE", 64) for portable code.