From 00d600f8b8c65923b5e4b55f74ffbd40952997c1 Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:24:30 +0200 Subject: [PATCH 1/6] Fix bootloop if nvs partition is missing or has non-default label --- esphome/components/esp32/preferences.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 925c4e7662..d2c7e26515 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -70,8 +70,13 @@ bool ESP32PreferenceBackend::load(uint8_t *data, size_t len) { } void ESP32Preferences::open() { - nvs_flash_init(); - esp_err_t err = nvs_open("esphome", NVS_READWRITE, &this->nvs_handle); + esp_err_t err = nvs_flash_init(); + if (err != 0) { + this->nvs_handle = 0; + return; + } + + err = nvs_open("esphome", NVS_READWRITE, &this->nvs_handle); if (err == 0) return; From e1a5e908753f23dd7c3b2219b6197f9db8f1dcba Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:58:00 +0200 Subject: [PATCH 2/6] Remove ESP_LOGW --- esphome/components/esp32/preferences.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index d2c7e26515..95948d7b3b 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -70,17 +70,13 @@ bool ESP32PreferenceBackend::load(uint8_t *data, size_t len) { } void ESP32Preferences::open() { - esp_err_t err = nvs_flash_init(); - if (err != 0) { - this->nvs_handle = 0; - return; - } - - err = nvs_open("esphome", NVS_READWRITE, &this->nvs_handle); + // Can't use ESP_LOG... in this function because it is called before the logger is initialized + nvs_flash_init(); + esp_err_t err = nvs_open("esphome", NVS_READWRITE, &this->nvs_handle); if (err == 0) return; - ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS", esp_err_to_name(err)); + // nvs_open failed, erasing NVS nvs_flash_deinit(); nvs_flash_erase(); nvs_flash_init(); From e819c1c57829f674698e029566adcd409caf12f4 Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:38:18 +0200 Subject: [PATCH 3/6] Fix WiFi if NVS failed to initialize --- esphome/components/wifi/wifi_component_esp_idf.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 29d135ce90..d91bc4ae6e 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -179,7 +179,10 @@ void WiFiComponent::wifi_pre_setup_() { #endif // USE_WIFI_AP wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - // cfg.nvs_enable = false; + if (global_preferences->nvs_handle == 0) { + ESP_LOGW(TAG, "NVS failed to initialize, starting WiFi with NVS disabled"); + cfg.nvs_enable = false; + } err = esp_wifi_init(&cfg); if (err != ERR_OK) { ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err)); From 7770190f55e01468f0de6e5524f059de4ef93cb6 Mon Sep 17 00:00:00 2001 From: Mat931 <49403702+Mat931@users.noreply.github.com> Date: Sun, 26 Apr 2026 20:50:07 +0200 Subject: [PATCH 4/6] Use shorter warning message to save memory --- esphome/components/wifi/wifi_component_esp_idf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index d91bc4ae6e..82ecc80811 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -180,7 +180,7 @@ void WiFiComponent::wifi_pre_setup_() { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); if (global_preferences->nvs_handle == 0) { - ESP_LOGW(TAG, "NVS failed to initialize, starting WiFi with NVS disabled"); + ESP_LOGW(TAG, "starting wifi without nvs"); cfg.nvs_enable = false; } err = esp_wifi_init(&cfg); From 98f250b62c403863b1d5362631abac0188ad6207 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Apr 2026 09:53:40 -0500 Subject: [PATCH 5/6] Defer nvs_open warning instead of dropping it Stash the err code in a static and emit ESP_LOGW lazily from the first make_preference() call (which runs after Logger::pre_setup() at EARLY_INIT priority), so the diagnostic stays visible without dereferencing global_logger before it is set. --- esphome/components/esp32/preferences.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 95948d7b3b..e49b688638 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -18,6 +18,12 @@ struct NVSData { static std::vector s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +// open() runs from app_main() before the logger is initialized, so any failure +// must be deferred until after global_logger is set. This is emitted from the +// first make_preference() call, which runs from the generated setup() after +// log->pre_setup() has run at EARLY_INIT priority. +static esp_err_t s_open_err = ESP_OK; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + bool ESP32PreferenceBackend::save(const uint8_t *data, size_t len) { // try find in pending saves and update that for (auto &obj : s_pending_save) { @@ -70,13 +76,14 @@ bool ESP32PreferenceBackend::load(uint8_t *data, size_t len) { } void ESP32Preferences::open() { - // Can't use ESP_LOG... in this function because it is called before the logger is initialized + // Runs from app_main() before the logger is initialized; any logging here + // must be deferred. See s_open_err and make_preference() below. nvs_flash_init(); esp_err_t err = nvs_open("esphome", NVS_READWRITE, &this->nvs_handle); if (err == 0) return; - // nvs_open failed, erasing NVS + s_open_err = err; nvs_flash_deinit(); nvs_flash_erase(); nvs_flash_init(); @@ -88,6 +95,10 @@ void ESP32Preferences::open() { } ESPPreferenceObject ESP32Preferences::make_preference(size_t length, uint32_t type) { + if (s_open_err != ESP_OK) { + ESP_LOGW(TAG, "nvs_open failed: %s - erased NVS", esp_err_to_name(s_open_err)); + s_open_err = ESP_OK; + } auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) pref->nvs_handle = this->nvs_handle; pref->key = type; From 8af10c0c976e36d4f4ed73f6cd6c674a8dca5a4c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Apr 2026 10:06:05 -0500 Subject: [PATCH 6/6] Distinguish erased-NVS from NVS-unavailable in deferred warning If the erase+reopen recovery also fails (e.g. missing/mislabeled NVS partition), nvs_handle stays 0. Report that state explicitly instead of claiming NVS was erased. --- esphome/components/esp32/preferences.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index e49b688638..09835385ac 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -96,7 +96,11 @@ void ESP32Preferences::open() { ESPPreferenceObject ESP32Preferences::make_preference(size_t length, uint32_t type) { if (s_open_err != ESP_OK) { - ESP_LOGW(TAG, "nvs_open failed: %s - erased NVS", esp_err_to_name(s_open_err)); + if (this->nvs_handle == 0) { + ESP_LOGW(TAG, "nvs_open failed: %s - NVS unavailable", esp_err_to_name(s_open_err)); + } else { + ESP_LOGW(TAG, "nvs_open failed: %s - erased NVS", esp_err_to_name(s_open_err)); + } s_open_err = ESP_OK; } auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)