Enhancing Thread Scheduling of Klipper (Patreon)
Content
One of the most frustrating issues I’ve faced over the past few years is the “mcu: timer too close” error. It occurs sporadically whenever a high enough CPU spike happend for a long enough time.
Today, I want to break down why this error happens, share what I’ve done to fix it in THEOS
🔥 Why Does This Error Occur?
The way Klipper communicates with the MCU is based on a bus system that sends and receives data every 25ms. Klipper keeps a close eye on the timing, and if the time delta between these packets becomes too short, the print stops with the "mcu: timer too close" error.
In most cases, the MCU itself isn’t the problem—it's the Klipper host that causes the bottleneck. So, I focused my time here on talking about the issues on the host side rather than the MCU.
Klipper’s Process Architecture
If you check Klipper’s processes (ps -x), you’ll see that Klipper runs as a service under a specific process ID.

Digging deeper (top -H -p <PID>), you’ll find that Klipper spawns multiple threads, but every single one is just labeled “python”—not very helpful.

This is where things get messy:
Klipper has multiple critical and non-critical threads
The serial communication threads (C++ and Python) must respond instantly to avoid the error
Less important threads (logging, frontend communication) don’t need high priority
The OS doesn’t know which threads are critical—so it treats them all the same
Without a way to prioritize which threads matter most, the system can’t prevent delays that trigger the error.
🛠 My Fix – Giving Threads Proper Prioritization
Over the last two days, I changed klipper to assigns proper names to Klipper’s threads. Now, instead of everything being a generic “python” thread, we can identify each one separately and give the important ones the priority they deserve:

🎯 How the Linux Scheduler Works
Linux processes operate in two priority zones:
Realtime (PR: -100 to -1) → Reserved for realtime & critical tasks
Normal (PR: 0 to 39) → Where Klipper lives
Each process has a niceness value (-20 to 19) that determines its priority within the normal zone. By default, Armbian starts Klipper at niceness 0 (priority 20).
I build a thread hierarchy that looks like this:
The C++ serial thread has the highest priority
The Python serial thread follows closely behind
Other Klipper threads have a standard priority
Logging/frontend communication has the lowest priority
With that in mind I implemented those hierarchy into klipper with the following result:

I didn’t push it to the extreme, because certain system processes (like garbage collection) need higher priority to prevent unexpected side effects. But Klipper’s serial communication is now more important than things like webcam streaming or background socket traffic.
🔬 Next Steps – Testing & THEOS 1.2.0
With these changes in place, I’m expecting a significant reduction (or complete elimination) of the “timer too close” error. Over the next 4–6 weeks, I’ll be stress-testing this change to see if any unintended side effects appear. If all goes well, this fix will be included in THEOS 1.2.0 as a core improvement.
This update should make THEOS the most robust klipper operating system! Also it should lower the hardware requirements.
🙌 Thank You for Your Support!
Matt