๐Ÿ€Zerve chosen as NCAA's Agentic Data Platform for 2026 Hackathonยท๐ŸงฎMeet the Zerve Team at Data Decoded Londonยท๐Ÿ“ˆWe're hiring โ€” awesome new roles just gone live!
Back
Visualization

RuntimeError: Main Thread is Not in Main Loop - How to Fix It

Answer

This error occurs when matplotlib's GUI backend tries to display a plot from a non-main thread, or when there's a conflict between backends. Fix it by switching to a non-interactive backend like Agg, using plt.savefig() instead of plt.show(), or ensuring plotting runs on the main thread.

Why This Happens

Matplotlib's interactive backends (TkAgg, Qt5Agg) require access to the main thread for GUI operations. When you run plotting code in a background thread, inside certain web frameworks, or in environments with conflicting event loops, this thread restriction triggers the error. It's common in Flask/Django apps, Jupyter with threading, or automated scripts.

Solution

The rule: if you're not displaying plots interactively, use matplotlib.use('Agg') before importing pyplot. Save figures with savefig() instead of show().

import matplotlib
import matplotlib.pyplot as plt

# โŒ Problematic: interactive backend in non-main thread
plt.plot([1, 2, 3])
plt.show()
# RuntimeError: main thread is not in main loop

# โœ… Fix 1: switch to non-interactive backend BEFORE importing pyplot
import matplotlib
matplotlib.use('Agg')  # must be before importing pyplot
import matplotlib.pyplot as plt

plt.plot([1, 2, 3])
plt.savefig('plot.png')  # save instead of show

# โœ… Fix 2: set backend in script or environment
# At the very top of your script:
import matplotlib
matplotlib.use('Agg')

# Or set environment variable before running:
# export MPLBACKEND=Agg

# โœ… Fix 3: for Jupyter notebooks
%matplotlib inline
# or
%matplotlib agg

# โœ… Fix 4: save figure without displaying
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
fig.savefig('output.png', dpi=150, bbox_inches='tight')
plt.close(fig)  # free memory

# โœ… Fix 5: for web frameworks (Flask, Django)
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from io import BytesIO
import base64

def create_plot():
    fig, ax = plt.subplots()
    ax.plot([1, 2, 3])
    
    buffer = BytesIO()
    fig.savefig(buffer, format='png')
    buffer.seek(0)
    plt.close(fig)
    
    return base64.b64encode(buffer.getvalue()).decode()

# โœ… Check current backend
print(matplotlib.get_backend())

Better Workflow

In Zerve, just write plt.plot() and plt.show(). That's it. Zerve handles the backend, rendering, and display automatically. No matplotlib.use('Agg') before importing. No thread safety management. No saving to file and opening manually. Traditional Python needs 10 lines of boilerplate before your first plot. Zerve needs zero. Focus on the insight, not the infrastructure.

Better workflow

Related Topics

Decision-grade data work

Explore, analyze and deploy your first project in minutes