The faulthandler module helps diagnose severe failures that can terminate a process before Python produces a normal traceback. It can dump Python stacks when the process receives a segmentation fault, abort, bus error, illegal instruction, timeout, or an operator-requested signal. It is especially useful in applications that rely on native extensions, scientific libraries, drivers, bindings, long-running services, multiprocessing, and tests that hang intermittently.
The module does not repair the failure and does not replace a native debugger. Its purpose is to preserve enough context to show where Python threads were when the process failed or stopped making progress. Because the runtime may already be corrupted, output is intentionally minimal and should be written to a reliable file or stream.
Enable faulthandler
The simplest approach is to call faulthandler.enable() near application startup.
import faulthandler
faulthandler.enable()
Output goes to sys.stderr by default. Enable it before importing suspicious native modules or starting worker threads so early crashes are captured too.
Enable it with an environment variable
Production systems may need diagnostics without a code deployment.
PYTHONFAULTHANDLER=1 python app.pyThis is convenient in containers, test commands, scheduled jobs, and incident response. It also works when a crash happens during imports before normal initialization reaches your configuration code.
Use the -X option
The interpreter accepts -X faulthandler.
python -X faulthandler app.pyDocument this switch in operational runbooks. A diagnostic feature is less useful if the team must research how to activate it during an outage.
Which failures are captured
On supported platforms, the module installs handlers for fatal signals such as SIGSEGV, SIGFPE, SIGABRT, SIGBUS, and SIGILL. Exact availability depends on the operating system and Python build.
These failures commonly indicate memory corruption, invalid native arithmetic, an explicit abort, an invalid address, or an unsupported machine instruction. Pure Python code rarely causes them directly; native extensions and external libraries are typical suspects.
Fatal dumps versus regular tracebacks
A regular Python exception unwinds frames, executes finally blocks, and can be caught. A fatal native error may stop the runtime immediately. Faulthandler tries to write stack information using a restricted implementation that does not depend on normal traceback machinery.
The result may omit local values and show only filenames, function names, and line numbers. That limited information is still often enough to identify the active subsystem.
Write to a dedicated file
Service stderr may be lost, truncated, or mixed with high-volume logs. Open a dedicated file and keep it alive for the entire time the handler is enabled.
import faulthandler
crash_log = open("python-faults.log", "a", buffering=1)
faulthandler.enable(file=crash_log, all_threads=True)
Do not close the file while faulthandler may still use it. The module retains the underlying descriptor and does not automatically reopen a replacement.
Log rotation
If an external rotator renames or replaces the file, the old descriptor may continue pointing to the previous inode. Reopen the destination and configure the handler again, or use stderr and let the container or service manager collect it.
Test the exact rotation strategy in the same environment used by production.
Dump all threads
The all_threads=True
faulthandler.dump_traceback(all_threads=True)
This is critical when one thread is waiting for another. The thread that requests the dump may not be the thread responsible for a deadlock.
Generate a manual dump
dump_traceback() writes stacks without terminating the process.
import faulthandler
faulthandler.dump_traceback()
Use it in protected administrative commands, internal watchdogs, support tooling, and controlled tests. Never expose dumps publicly because source paths and architecture details can be sensitive.
Schedule a timeout dump
dump_traceback_later() schedules a dump if an operation exceeds a deadline.
faulthandler.dump_traceback_later(
30,
repeat=False,
file=crash_log,
exit=False,
)
try:
run_operation()
finally:
faulthandler.cancel_dump_traceback_later()
This pattern is valuable for tests that sometimes hang, slow startup, blocked shutdown, and native calls that fail to return.
Repeat dumps
With repeat=True, the module writes stacks at regular intervals.
Several snapshots help distinguish a static deadlock from a slow process that is still moving. Repeated dumps can create substantial volume, so define retention and cancel the timer as soon as the incident ends.
Exit after a timeout
The exit=True
Use it only when a supervisor can replace the process and continued execution would be unsafe. Immediate exit skips ordinary cleanup, may leave buffers unflushed, and can interrupt transactions.
Register an operator signal
On Unix-like systems, register() can attach a dump to a user signal.
import faulthandler
import signal
faulthandler.register(
signal.SIGUSR1,
all_threads=True,
chain=False,
)
An authorized operator can then send the signal to a PID and obtain stacks without stopping the process.
Choose the signal carefully
Do not overwrite a signal already used by the application, server, runtime, or observability agent. Document the selected signal and test for conflicts.
Inside containers, verify that the signal reaches the Python process. A shell running as PID 1 may not forward it as expected.
The chain parameter
With chain=True, the previous signal handler runs after the dump. This can preserve another diagnostic tool, but it may also execute logic that is unsafe in signal context.
Test the combination on the same platform and runtime used in production.
Unregister a signal
unregister(signum) removes the module’s handler.
faulthandler.unregister(signal.SIGUSR1)
Use it when unloading a plugin, changing diagnostics, or cleaning up tests that modify global signal state.
Check whether it is enabled
is_enabled() reports whether fatal-error handlers are active.
if not faulthandler.is_enabled():
faulthandler.enable()
This is helpful in executables and frameworks. A reusable library should generally let the application decide whether to install global handlers.
Avoid hidden activation in libraries
A library should not silently replace stderr behavior, signal handlers, or global files. Offer an explicit configuration hook and let the executable own the decision.
Global handlers may conflict with notebooks, test runners, embedded interpreters, and application servers.
Test-runner integration
Enable the module when tests involve native extensions, threads, or subprocesses.
python -X faulthandler -m pytestFor a test that can hang, schedule a deadline slightly longer than the expected duration. The resulting stacks often identify the fixture, lock, queue, or native call that is stuck.
Multiprocessing
Every process has its own interpreter and must enable faulthandler independently. A parent-process dump does not automatically show child threads.
def initialize_worker():
faulthandler.enable()
Use separate files by PID or a centralized stderr collector so simultaneous outputs remain understandable.
Threads
All-thread dumps reveal locks, conditions, queues, and blocking I/O. Give threads descriptive names to improve related logs and debugger views.
threading.Thread(
target=worker,
name="customer-importer",
)
Faulthandler focuses on frames, but consistent naming improves the rest of the incident evidence.
Concurrent futures
A thread pool can appear frozen when every worker waits for another future from the same executor. Repeated dumps show many threads blocked in Future.result().
See Python concurrent.futures for structural deadlock prevention.
Native extensions
If the final Python frame calls a native extension, the underlying fault may be inside C, C++, Rust, Fortran, a GPU runtime, or a driver. Record library versions, architecture, operating system, inputs, and reproduction steps.
The Python stack identifies the native boundary, but GDB, LLDB, WinDbg, sanitizer builds, or vendor tools may still be required.
C stack support
Recent versions and selected builds can provide additional native-stack information in supported environments. Availability depends on platform, build options, and debug symbols.
Treat native-stack output as a supplement. Unstripped binaries and symbol packages make it far more useful.
File descriptor lifetime
The module writes through the descriptor associated with the stream. If the file is closed and that descriptor number is reused, output may be written to an unrelated destination.
Keep ownership explicit and close the file only after disabling the handler or terminating the process.
Disable when required
disable() removes fatal handlers installed by the module.
faulthandler.disable()
This may be necessary in tests for custom handlers or embedded applications. In ordinary services, leaving it enabled has little overhead until a dump is requested.
Dump security
Dumps deliberately omit many local values, but they can still reveal filesystem paths, internal function names, customer-specific modules, and service architecture.
Protect files, limit access to diagnostic signals, apply retention rules, and sanitize reports before posting them publicly.
Privacy in multi-tenant services
An all-thread dump may reveal which operations several customers are running at the same time. Treat it as sensitive operational data.
Do not expose dump generation through an unauthenticated endpoint. Require strong authorization and audit every use.
Watchdogs
An external watchdog can request a dump before restarting an unresponsive process. This preserves evidence while allowing automated recovery.
Use two deadlines: first diagnostics, then termination. The process gets an opportunity to recover, and the operator receives context if it remains stuck.
Deadlock versus slow progress
One dump shows a position; several dumps show movement. Changing line numbers suggest slow progress. Identical frames in waits and locks suggest a deadlock or a permanently blocked external operation.
Combine stacks with CPU, I/O, queue depth, memory, and latency metrics.
It does not replace logging
Logs explain the semantic sequence before a failure. Faulthandler shows thread positions at a critical instant. Use both.
Include operation IDs and component versions in logs so a dump can be matched to a request or job.
It does not replace core dumps
A core dump preserves native memory and process state for deep analysis, but it is larger and can contain secrets. Faulthandler is lightweight and fast.
For difficult incidents, configure both according to security and retention policy.
Limitations
No handler runs after SIGKILL, a power loss, or some immediate external termination. Severe corruption can also prevent complete output.
The module does not automatically detect every deadlock and cannot explain the internal state of arbitrary native libraries.
Test the diagnostic path
Do not wait for a real crash. Trigger manual dumps in a controlled environment and verify the destination, permissions, rotation, collection, and alerting.
Repeat the test in containers, service managers, Windows services, and multiprocessing workers.
Common mistakes
Common failures include enabling too late, closing the handler file, assuming a timeout stops work without exit, registering a conflicting signal, exposing dumps publicly, expecting complete native details, and enabling only in the parent process.
Conclusion
faulthandler is a simple and valuable diagnostic layer for crashes, deadlocks, and hangs. Enable it early, preserve output in a reliable destination, use signals or timeouts for on-demand snapshots, and include all threads.
Combine the report with logs, metrics, and native debuggers when C extensions are involved. Consult the official faulthandler documentation and Python dis for inspecting bytecode near a failing path.







