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.
)
&w=1200&q=75)
&w=1200&q=75)
&w=1200&q=75)