If you've ever run a Python script and seen it die with:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
…you've hit a segmentation fault. Your Python process crashed at the C level, and no Python traceback was printed because the interpreter itself was killed by the operating system. This article walks through what SIGSEGV means, the most common causes (based on the popular Stack Overflow thread Process finished with exit code 139), and — most importantly — the #1 cause and its fix: using a non-interactive matplotlib backend.
for more Python or Pycharm errors check:
What does SIGSEGV actually mean?
SIGSEGV (signal 11, "segmentation violation") is sent by the OS when a process tries to read from or write to a memory address that isn't mapped into its address space.
In a pure-Python program this is almost never a bug in your code — Python raises exceptions for that. A segfault usually means:
- A C extension module (NumPy, OpenCV, TensorFlow, PyTorch, wxPython, PyQt, matplotlib's GUI backends,
pyodbc,scikit-learn, etc.) did something illegal with memory. - The Python interpreter itself was built with a broken toolchain.
- Two native libraries in the same process are conflicting (e.g. two different OpenMP runtimes, two Qt versions).
Because the crash happens below Python, you get no traceback — just the exit code.
Cause #1 — matplotlib using an interactive GUI backend (the most common)
This is the single most frequent cause in IDE environments like PyCharm, VS Code, and Jupyter on macOS and Linux, and it happens even in scripts that "should" work.
The problem
By default, matplotlib picks a GUI backend based on your OS:
| OS | Default backend |
|---|---|
| macOS | MacOSX (Cocoa) |
| Windows | TkAgg or QtAgg |
| Linux | TkAgg or QtAgg |
These backends spin up a native event loop and require a main thread with a running windowing system. When you:
- run a script inside an IDE debugger,
- mix matplotlib with Selenium, wxPython, PyQt, or threads,
- run on a headless server or over SSH,
- call
plt.show()after the GUI thread has already been torn down,
…the backend tries to talk to a GUI that doesn't exist or has already been destroyed → SIGSEGV.
Minimal reproduction
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [4, 5, 6])
plt.show()
On macOS in PyCharm this frequently crashes with exit code 139 the moment plt.show() is called, or right after the window is closed.
The fix — use the Agg backend
Agg is a pure C, non-interactive rasterizer. It has no GUI, no event loop, no windowing system dependency. It cannot segfault on show() because show() is a no-op.
import matplotlib
matplotlib.use("Agg") # MUST come BEFORE importing pyplot
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [4, 5, 6])
plt.savefig("chart.png") # <-- save instead of show
Three rules to make this work:
- Set the backend before importing
pyplot.pyplotbinds to a backend the moment it's imported. If you doimport matplotlib.pyplot as pltfirst and thenmatplotlib.use("Agg"), it's too late — you'll get a warning and the GUI backend stays active. - Replace
plt.show()withplt.savefig(...).Agghas no window to show. - Call
plt.close(fig)after saving to release the C-side memory.
Crash-safe pattern
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [4, 5, 6])
plt.savefig("chart.png", dpi=150, bbox_inches="tight")
plt.close(fig) # free memory
print("✅ Saved chart.png")
If you're in a Jupyter notebook and still want inline figures, use:
%matplotlib inline
or, if you're on a headless server:
%matplotlib agg
Bonus: how to verify which backend is active
import matplotlib
print(matplotlib.get_backend())
If this prints MacOSX, TkAgg, or QtAgg, and your script crashes, switch to Agg.
Bonus: environment-variable override
You can also force the backend globally without touching the code:
export MPLBACKEND=Agg
python your_script.py
or, per-run:
MPLBACKEND=Agg python your_script.py
This is handy in CI pipelines and Docker images.
Cause #2 — Mixing Selenium (or any GUI library) with matplotlib
Even with Agg, if you run Selenium and matplotlib in the same Python process, you can still get SIGSEGV on shutdown. The crash usually happens after your script finishes, when Python tears down the interpreter and both C libraries try to free the same resources.
Fix: split the workflow, or use os._exit(0)
Option A — split into two scripts:
| Script | Job |
|---|---|
scrape.py |
Selenium only → writes data.json, then driver.quit() |
analyze.py |
pandas + matplotlib only → reads data.json, builds chart |
Option B — one script, but skip interpreter teardown:
import os
# ... everything runs here ...
driver.quit()
plt.savefig("chart.png")
plt.close("all")
os._exit(0) # skip Python's atexit/GC — this is where SIGSEGV often fires
os._exit(0) bypasses normal cleanup and exits the process immediately.
Other common causes reported on the Stack Overflow thread
The SIGSEGV question has accumulated 24+ answers over the years, each pointing at a different culprit. Here's the complete map, grouped by category:
1. GUI / windowing libraries
- wxPython — destroying a frame while the main loop is running (the original question).
- PyQt / PySide — turning off PyQt compatibility in the PyCharm debugger actually fixes it for many users. In PyCharm:
Settings → Build, Execution, Deployment → Python Debugger → Collect runtime types— toggle it. - matplotlib — GUI backend mismatch (see Cause #1).
2. Deep-learning frameworks
- TensorFlow — new versions crash on older CPUs/GPUs. Fix: downgrade (e.g.
tensorflow==1.4, ortensorflow-gpu==1.9.0). - PyTorch — versions > 2.0.1 crash on some Macs. Downgrade or upgrade to a build matching your macOS version.
- MONAI — importing
monaicrashed until a fresh conda environment was created. Naming the env the same as the package also caused issues.
3. Scientific / ML stack
- scikit-learn — KMeans segfaulted on some versions. Fix: upgrade to
1.0.2+, orpip uninstall scikit-learn && pip install scikit-learn. - NumPy — version 1.24.3 fixed a SIGSEGV triggered by
import cv2. Downgrading NumPy to1.24.3is a widely reported fix. - OpenCV —
cv2.SIFT()→cv2.SIFT_create(). Alsocv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml").
4. GPU / hardware
- PyOpenCL — pointing at a GPU context that isn't connected. Fix: change
os.environ['PYOPENCL_CTX']to the right index. - General memory issues — rebooting the machine resolved it in a few cases.
5. Database drivers
- pyodbc / Oracle — opening the same connection multiple times, or hitting a breakpoint while the connection is open. Fix: open once and reuse, or close properly after use.
6. Concurrency
concurrent.futures— nesting.map()inside another.map()call. Fix: remove one of the.mapcalls.requestswith a client certificate in threads — switched tohttpxand the crash disappeared.
7. File / I/O
- Writing to a file that's still open — close the file and rerun.
- Pickled pandas DataFrame — corrupted
.pfile. Delete and regenerate.
8. Environment / packaging
- Corrupted virtualenv — delete the interpreter and the
venv/folder, recreate. - Recent conda installs — revert the most recent
conda installcommands. - Conflicting libraries —
pysparkin an env with other big-data packages. - PHP / PHPUnit — a circular dependency (yes, other languages report the same exit code).
9. Debugging tools
- GDB — run
gdb --args python your_script.py, thenruninside GDB. When it crashes, usebtto see the C stack trace and identify the offending library. faulthandler—python -X faulthandler your_script.pyprints a Python-level C traceback at crash time. The fastest way to identify which extension module died.
10. Ignoring the signal (not recommended)
Some answers suggest:
import signal
signal.signal(signal.SIGSEGV, signal.SIG_IGN)
Don't do this. Ignoring SIGSEGV leaves your process in an undefined state — memory corruption will silently propagate and produce wrong results. Fix the root cause instead.
Quick decision tree
SIGSEGV in Python?
│
├─ Does the script use matplotlib?
│ └─ YES → Set matplotlib.use("Agg") BEFORE importing pyplot.
│ Replace plt.show() with plt.savefig().
│ Add plt.close(fig) and os._exit(0).
│
├─ Does the script use Selenium / PyQt / wxPython?
│ └─ YES → Split the script, or exit with os._exit(0).
│ In PyCharm, toggle "Collect runtime types" in the debugger.
│
├─ Does the script use TensorFlow / PyTorch / OpenCV / sklearn?
│ └─ YES → Check versions. Downgrade or upgrade the offending package.
│ Try a fresh conda/venv environment.
│
├─ Does the script use threads or concurrent.futures?
│ └─ YES → Remove nested .map() calls.
│
└─ Still crashing?
└─ Run with faulthandler: python -X faulthandler script.py
Or with GDB: gdb --args python script.py
TL;DR
| Symptom | Most likely cause | Fix |
|---|---|---|
Crash on plt.show() |
GUI backend (MacOSX/TkAgg/QtAgg) | matplotlib.use("Agg") before importing pyplot |
| Crash on IDE shutdown | Interpreter teardown conflict | End script with os._exit(0) |
| Crash with Selenium + matplotlib | Mixed C libraries | Split into two scripts |
| Crash importing TF/PyTorch/cv2 | Version mismatch | Downgrade/upgrade the package |
| Crash in PyCharm debugger | Debugger bug | Toggle "Collect runtime types" |
| Crash with no pattern | Corrupted venv / env | Delete and recreate the environment |
The single most impactful fix — and the one that resolves the majority of cases in IDE environments — is:
import matplotlib
matplotlib.use("Agg") # MUST come before importing pyplot
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [4, 5, 6])
plt.savefig("chart.png") # use savefig, not show
Do that, add plt.close(fig) and os._exit(0), and the vast majority of SIGSEGV crashes in data-science and scraping scripts disappear.