How Do You Decode an ESP32 Guru Meditation Error and Backtrace?
Last updated 4 August 2026 · 7 min read
Direct Answer
An ESP32 Guru Meditation Error is the panic banner printed when the Xtensa core hits an unrecoverable exception. The line names the specific cause (LoadProhibited, StoreProhibited, IllegalInstruction, and others), and the register dump and Backtrace line that follow it can be resolved to actual source file and line numbers using idf.py's built-in monitor decoder or the toolchain's addr2line, turning a raw crash into an exact function and call chain.
Detailed Explanation
When an ESP32 running ESP-IDF (or Arduino-ESP32, which uses the same underlying panic handler) hits an exception the CPU cannot recover from, it prints a distinctive banner starting with Guru Meditation Error: before resetting. The full line identifies which core panicked and why, for example:
Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
The word in parentheses is the exception cause, and it's the single most useful piece of information in the entire panic output: it tells you what class of bug you're dealing with before you've looked at a single register.
Common Exception Causes
| Cause | Typical meaning |
|---|---|
LoadProhibited | Code tried to read from an address with no valid mapping, commonly a null or dangling pointer dereference. |
StoreProhibited | Code tried to write to an address with no valid mapping. Same usual root cause as LoadProhibited, but on a write. |
IllegalInstruction | The CPU tried to execute a bit pattern that isn't a valid instruction, commonly the result of a corrupted function pointer or a jump into data rather than code. |
InstrFetchProhibited | The CPU tried to fetch an instruction from an address it can't execute from, similar in practice to IllegalInstruction but specifically an instruction-fetch failure. |
Unhandled debug exception | A breakpoint instruction executed with no debugger attached to service it, sometimes from an assert macro that inserts a break instruction. |
Integer divide by zero | A division by zero occurred in code that traps on it. |
LoadProhibited and StoreProhibited account for the large majority of Guru Meditation panics seen in real projects, and both point toward the same family of bugs: a null pointer, a pointer used after the memory it referenced was freed, or an array index that ran outside its bounds and landed on an address with no valid mapping.
Reading the Register Dump and Backtrace
Immediately after the panic banner, ESP-IDF prints the CPU's register state at the moment of the fault (PC, PS, the A0–A15 address registers, and EXCVADDR, the faulting address for memory-access exceptions), followed by a Backtrace: line listing a sequence of address pairs walking back up the call stack.
On its own, this backtrace is just a list of hexadecimal addresses, not useful without the matching build's symbol information. There are two ways to resolve it:
idf.py monitor's automatic decoder. If you have the ELF file from the exact build that produced the crash (the default location isbuild/<project>.elf), runningidf.py monitorwhile the device panics decodes the backtrace automatically, printing function names and, with debug symbols enabled, source file and line numbers directly beneath the raw addresses.- Manual
xtensa-esp32-elf-addr2line. For a backtrace captured after the fact (from a log file, or a device you can't currently attach a monitor session to), the toolchain'saddr2lineutility resolves each address individually:xtensa-esp32-elf-addr2line -pfiaC -e build/<project>.elf <address> <address> ..., passing every address from the backtrace line in order.
Either way, the ELF file has to be the exact one that produced the crashing firmware. A backtrace decoded against a different build's ELF (even a build that's only a few commits newer or older) resolves to plausible-looking but wrong function names and lines, which is a common source of wasted debugging time when a project doesn't keep build artefacts matched to deployed firmware versions.
A Worked Example
A typical panic block, trimmed to the essentials, looks like this:
Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400d1a3c PS : 0x00060530 A0 : 0x800d21f4 A1 : 0x3ffb4f90
EXCVADDR: 0x0000001c
Backtrace: 0x400d1a3c:0x3ffb4f90 0x400d21f1:0x3ffb4fb0 0x400d3402:0x3ffb4fe0
Working through it: the exception cause is LoadProhibited, so this is a bad-read bug before anything else is examined. EXCVADDR is 0x0000001c, which is 28 decimal, not a stack, heap, or peripheral address by any reasonable coincidence. An address that small and specific is the classic signature of a null-pointer struct-member dereference: code read some_struct->field where some_struct was null, and field's byte offset within the struct (28 in this case) became the literal address the CPU tried to read from.
Running the backtrace addresses through xtensa-esp32-elf-addr2line -pfiaC -e build/app.elf 0x400d1a3c 0x400d21f1 0x400d3402 resolves each address to a function, file, and line, typically pointing straight at the dereferencing line inside whichever function called into the code that used the null pointer. Between the exception cause, EXCVADDR's small offset value, and the resolved call chain, this combination is usually enough to identify the bug without needing to reproduce it under a live debugger.
Using esp_core_dump in Production
idf.py monitor only works with a live, attached serial connection, which isn't available once a device has shipped. ESP-IDF's esp_core_dump component solves this for production by capturing the full register state, backtrace, and (optionally) a memory snapshot to a dedicated flash or UART core dump partition when a panic occurs.
Enabling it requires a coredump partition entry in the project's partition table and the corresponding CONFIG_ESP_COREDUMP_* Kconfig options (flash-based storage is the common choice for field devices, since it survives a power cycle; UART-based dumps only work if something is actively listening on the port at the moment of the crash). After a device panics and later reconnects, idf.py coredump-info reads the stored dump and produces the same kind of decoded backtrace and register report idf.py monitor would give for a live crash, using the same matched-ELF requirement described above.
For field-deployed devices, this is the practical difference between "the device rebooted for no reason" and an actual, analysable crash report: without a core dump mechanism, a field panic that self-recovers via reset leaves no diagnostic trail at all once the device is back online.
Design Considerations
- Enable
esp_core_dumpwith flash storage before shipping, not after the first unexplained field crash. A panic that self-recovers via reset is invisible without it. See how to debug embedded firmware for the broader diagnostic toolkit this fits into. - Archive the ELF file for every firmware version you ship, keyed to its version string or build identifier, since decoding a field crash report is only possible against the exact matching ELF.
- Treat
LoadProhibited/StoreProhibitedas pointer bugs first. Check recently freed memory, uninitialised pointers, and array bounds before looking elsewhere; these two causes cover most real-world panics. - Don't confuse a Guru Meditation panic with a watchdog reset when triaging a crash report. The panic banner and cause name tell you immediately which failure class you're looking at; see why does my ESP32 keep brownout-resetting for how to distinguish a panic from a brownout or watchdog reset by the boot log signature.
- Firmware crash diagnostics: Zeus Design's embedded firmware team builds production fault-capture and remote diagnostics into commercial ESP32 and other embedded products, so field crashes produce an actual report rather than a silent reboot.
Common Mistakes
- Discarding the ELF file after a release build without archiving it against that firmware version, making a later field crash report undecodable.
- Assuming the top address in the backtrace is always the exact faulting line. For some exception types the reported PC can be an instruction or two removed from the true fault site, so treat it as a strong starting point rather than an absolute certainty.
- Shipping without
esp_core_dumpenabled, then discovering that a field panic left nothing to diagnose beyond "the device restarted." - Decoding a backtrace against the wrong build's ELF file and trusting the resulting function names, rather than confirming the ELF matches the exact firmware version that produced the crash log.
Frequently Asked Questions
- Does this apply to the RISC-V ESP32 variants (C3, C6)?
- Partly. The RISC-V-based ESP32 variants (ESP32-C3, ESP32-C6, and others) still print a panic banner and backtrace on a fatal exception, and the same idf.py monitor decoding and core dump tooling apply, but the underlying exception model is RISC-V's trap-cause mechanism rather than the Xtensa exception-cause table described here, so the specific cause names and register set differ. Check the panic output's exception cause name against the RISC-V ISA's trap causes rather than the Xtensa table in this guide if you're on a RISC-V variant.
- Why does my backtrace show addresses instead of function names?
- Because the raw serial output only has the addresses the CPU actually saw; resolving them to function names and source lines requires the matching unstripped ELF file from the exact build that produced the crash, plus a symbol-resolution step, either idf.py monitor's automatic decoder or a manual addr2line pass against that ELF file. A backtrace decoded against the wrong build's ELF file will produce confidently wrong answers rather than an obvious error, so always match the ELF to the firmware build that actually crashed.
- Is a Guru Meditation Error the same thing as a watchdog reset?
- No. A Guru Meditation Error is triggered by the CPU itself hitting an exception it cannot handle, such as dereferencing a bad pointer. A watchdog reset (task watchdog or interrupt watchdog) happens when a task or ISR fails to yield in time, and prints a different banner naming the specific watchdog and the task that missed its deadline. Both are fatal-error paths on the ESP32, but they point at different classes of bug and are diagnosed differently.
References
Related Questions
Why Does My ESP32 Keep Brownout-Resetting?
An ESP32 brownout reset means the 3.3V rail sagged below the BOD threshold, not a firmware crash. Here's how to diagnose the cause and fix the power design.
How Do You Enable ESP32 Secure Boot and Flash Encryption for Production?
ESP32 Secure Boot v2 and flash encryption protect firmware from tampering and extraction — but the eFuse decisions are permanent. Here's how to plan them.
How Do You Debug Embedded Firmware?
Covers JTAG/SWD hardware debugging, printf over UART or SWO trace, and logic analyser use for embedded firmware on STM32, ESP32, and other MCU platforms.
How Do You Decode a Cortex-M HardFault Using the CFSR, HFSR, and MMFAR/BFAR Registers?
A Cortex-M HardFault leaves diagnostic data in the CFSR, HFSR, MMFAR, and BFAR registers plus a stacked register frame. Here's how to read them.
What Is a Watchdog Timer and How Do You Use It?
A watchdog timer resets an MCU when firmware hangs. Covers IWDG vs WWDG on STM32, prescaler setup, kick strategy, and window mode for fault detection.
ESP-IDF vs Arduino for ESP32: Which Framework Should You Use?
ESP-IDF gives full FreeRTOS control and is production-grade; Arduino is faster to start. Covers the differences, limitations of each, and when to switch.
Related Forum Discussions
ESP32 Matter device advertises fine over BLE but commissioning fails every time — stale QR code after a firmware rebuild?
Bringing up my first Matter product on an ESP32-C6 using esp-matter (built on ESP-IDF 5.2). It's a basic on/off light accessory for now, jus
ESP32 keeps dropping Wi-Fi after 20–30 minutes in deployed location — reconnect loop doesn't always recover
Having a frustrating one. Built an ESP32 environmental monitor (SHT40 temp/humidity, reports to an MQTT broker every 5 minutes). Works flawl
Is a double-sided PCB enough for a simple ESP32 sensor board, or should I go multi-layer?
Building a little battery-powered sensor board around an ESP32 module (the kind with the PCB antenna already built into the module, not desi