From 2ab7f032a8aee0ece47708f6bfb4d4d0f664a59a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 8 Apr 2026 09:31:30 -1000 Subject: [PATCH] [esp32] Preserve crash data across OTA rollback reboots Don't clear the crash data magic marker at boot time. Previously, crash_handler_read_and_clear() would clear the magic immediately, so if OTA rollback triggered a reboot before an API client connected, the crash trace was lost. Now the magic is only cleared after crash_handler_log() delivers the data to an API client via crash_handler_clear(). --- esphome/components/api/api_connection.h | 1 + esphome/components/esp32/crash_handler.cpp | 9 +++++++-- esphome/components/esp32/crash_handler.h | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 2f685b0b8a..4be5a73e81 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -276,6 +276,7 @@ class APIConnection final : public APIServerConnectionBase { App.schedule_dump_config(); #ifdef USE_ESP32_CRASH_HANDLER esp32::crash_handler_log(); + esp32::crash_handler_clear(); #endif #ifdef USE_RP2040_CRASH_HANDLER rp2040::crash_handler_log(); diff --git a/esphome/components/esp32/crash_handler.cpp b/esphome/components/esp32/crash_handler.cpp index ecf30d7878..05be0e1408 100644 --- a/esphome/components/esp32/crash_handler.cpp +++ b/esphome/components/esp32/crash_handler.cpp @@ -101,12 +101,17 @@ void crash_handler_read_and_clear() { if (s_raw_crash_data.pseudo_excause > 1) s_raw_crash_data.pseudo_excause = 0; } - // Clear magic regardless so we don't re-report on next normal reboot - s_raw_crash_data.magic = 0; + // Don't clear magic here — crash data must survive OTA rollback reboots. + // Magic is cleared by crash_handler_clear() after an API client receives the data. } bool crash_handler_has_data() { return s_crash_data_valid; } +void crash_handler_clear() { + s_raw_crash_data.magic = 0; + s_crash_data_valid = false; +} + // Look up the exception cause as a human-readable string. // Tables mirror ESP-IDF's panic_arch_fill_info() which uses local static arrays // not exposed via any public API. diff --git a/esphome/components/esp32/crash_handler.h b/esphome/components/esp32/crash_handler.h index 97a4d4e116..c5e7d145ec 100644 --- a/esphome/components/esp32/crash_handler.h +++ b/esphome/components/esp32/crash_handler.h @@ -4,12 +4,18 @@ namespace esphome::esp32 { -/// Read crash data from NOINIT memory and clear the magic marker. +/// Read and validate crash data from NOINIT memory. +/// Does not clear the magic marker — call crash_handler_clear() after +/// the data has been delivered to an API client so it survives OTA rollback reboots. void crash_handler_read_and_clear(); /// Log crash data if a crash was detected on previous boot. void crash_handler_log(); +/// Clear the magic marker and mark crash data as consumed. +/// Call after the data has been delivered to an API client. +void crash_handler_clear(); + /// Returns true if crash data was found this boot. bool crash_handler_has_data();