Home / Errors / main-thread-not-in-main-loop

Main thread is not in main loop

You touched a Tkinter widget from a background thread. Tk is not thread safe. Post the work back with after().

RuntimeError: main thread is not in main loop

What went wrong

def work():
    result = slow_thing()
    label.config(text=result)

threading.Thread(target=work).start()

The thread updated a widget directly. Tk only accepts calls from the thread that created it, and the error sometimes appears much later than the offending line, which makes it confusing to track down.

The fix

import threading

def work():
    result = slow_thing()
    root.after(0, lambda: label.config(text=result))

threading.Thread(target=work, daemon=True).start()

after(0, ...) puts the call on Tk's own queue, so it runs on the main thread at the next opportunity. Every widget update from a thread has to go through it.

A queue for anything bigger

import queue, threading

results = queue.Queue()

def work():
    for n in range(100):
        results.put(n)

def drain():
    while not results.empty():
        bar["value"] = results.get()
    root.after(100, drain)

threading.Thread(target=work, daemon=True).start()
drain()

The thread only ever puts things in the queue. The main loop checks the queue on a timer. Nothing in the thread touches a widget.

Why not just avoid threads

Often you can. If the slow work splits into pieces, schedule them with after instead and skip threading entirely. See events and bindings.

Still stuck

The PIL to Pillow converter finds most of these automatically, and the other error pages cover the rest.