A task ran so long that the idle task never got a turn, so the watchdog reset the chip. The usual cause is a loop that waits for something without yielding.
What the message means
The ESP32 runs FreeRTOS. A watchdog expects the idle task on each core to run regularly. The message “Task watchdog got triggered. The following tasks did not reset the watchdog in time” names the task that hogged the core.
Fix it in this order
- Read which task the message names, and which core. The Arduino loop() runs on core 1.
- Find any while loop that waits for a sensor, a pin or a network reply with no delay.
- Add a short delay(1) or yield() inside that loop so other tasks can run.
- Replace blocking waits with a time-out, so the loop always ends.
- Move heavy work, such as large calculations, into its own task with vTaskDelay between steps.
- Avoid long delay() calls inside interrupt handlers and callbacks. Set a flag instead and handle it in loop().
Why it happens
The task watchdog resets the chip when a task keeps the processor busy for too long without letting other tasks run. A loop() that never yields, a long blocking wait or a tight while loop can trigger it; the default timeout is a few seconds.
Break long work into steps, add a short delay(1) or yield() inside waiting loops, and use vTaskDelay() in your own tasks. Keep heavy work out of interrupt handlers.
If you see this, do this
| You see | Do this |
|---|---|
| The task named is IDLE | Another task is hogging the core. |
| It happens right after Wi-Fi connects | Look for a blocking wait in your connection code. |
| It happens at one fixed point | A loop at that point never ends or never yields. |
Related
ESP32 LoadProhibited, MQTT on the ESP32.
Last reviewed: November 2026. These are the usual causes and the order that finds them fastest. Test on your own board; clones and cables differ.
Why this happens
The ESP32 runs FreeRTOS, and each core has an idle task that must get processor time now and then. The task watchdog watches for that. Your Arduino loop() runs as a normal task. If it keeps the core busy for longer than the timeout without yielding, the idle task starves, the watchdog fires and the chip prints the message and resets.
A loop that waits using delay() is safe, because delay() hands the core to other tasks. A loop that spins on a condition with no delay, for example while the sensor is not ready, or a long computation, never yields. Library calls without a timeout, such as a network request to an unreachable server, can do the same.
How to confirm the cause
- Read the message: it names the task and the core. loopTask is your loop(); an IDLE task listed as starved means something else hogged that core.
- Add a Serial.println before and after suspected steps and see where the output stops.
- Search the sketch for while loops with an empty body or a condition that depends on hardware.
- Time the suspected function with micros() and print the result. Anything over a few seconds is a problem.
- Decode the backtrace printed with the message, if present, to see the exact function.
Worked example
Replacing an unbounded wait with one that gives up and lets other tasks run:
// Blocks forever if the sensor never answers:
// while (!sensor.ready()) {}
uint32_t t0 = millis();
while (!sensor.ready() && millis() - t0 < 500) {
delay(1); // yields to other tasks
}
if (!sensor.ready()) {
Serial.println("sensor timeout");
}Common mistakes
- Turning the watchdog off for the loop task and calling that a fix; the program is still blocked.
- Putting delays inside interrupt handlers, which is not allowed.
- Doing a long network request without a timeout.
- Processing a large array in one go instead of in steps.
- Waiting on Serial.available() in a tight loop.
If this did not fix it
- A stack overflow or an infinite recursion, which produces a different message but may follow.
- A library that blocks, such as a display or sensor driver; look for a timeout setting.
- Flash writing or heavy logging that takes longer than the timeout on the same core.