From 1ee478c41978d342b36d8b1c6935fe0ba6e3191e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 Mar 2026 10:19:19 -1000 Subject: [PATCH] [logger] Fix ESP8266 crash with VERY_VERBOSE log level Move logger codegen priority above platform init so that global_logger is set before setup_preferences() or any other platform code that may contain ESP_LOG* calls. Previously the logger initialized at DIAGNOSTICS priority (90), which ran after PLATFORM (1000). With VERY_VERBOSE, the ESP_LOGVV in ESP8266Preferences::setup() fired before global_logger was set, causing a nullptr dereference crash. Also hardcode `using namespace esphome;` in writer.py so it precedes all variable declarations regardless of codegen priority ordering. Fixes #14970 --- esphome/components/logger/__init__.py | 7 ++++--- esphome/core/config.py | 4 +++- esphome/coroutine.py | 10 ++++++++-- esphome/writer.py | 3 +++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 675f9a2ca4..54629465da 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -56,6 +56,7 @@ from esphome.const import ( PlatformFramework, ) from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority +from esphome.types import ConfigType CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") @@ -323,9 +324,9 @@ CONFIG_SCHEMA = cv.All( ) -@coroutine_with_priority(CoroPriority.DIAGNOSTICS) -async def to_code(config): - baud_rate = config[CONF_BAUD_RATE] +@coroutine_with_priority(CoroPriority.LOGGER_INIT) +async def to_code(config: ConfigType) -> None: + baud_rate: int = config[CONF_BAUD_RATE] level = config[CONF_LEVEL] CORE.data.setdefault(CONF_LOGGER, {})[CONF_LEVEL] = level initial_level = LOG_LEVELS[config.get(CONF_INITIAL_LEVEL, level)] diff --git a/esphome/core/config.py b/esphome/core/config.py index e112720f2b..e02c6ec75f 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -587,7 +587,9 @@ async def _add_looping_components() -> None: @coroutine_with_priority(CoroPriority.CORE) async def to_code(config: ConfigType) -> None: - cg.add_global(cg.global_ns.namespace("esphome").using) + # using namespace esphome is hardcoded in writer.py to guarantee it + # precedes all variable declarations regardless of coroutine priority. + # These can be used by user lambdas, put them to default scope # picolibc (IDF 6.0+) declares isnan in global scope, conflicting with using std::isnan cg.add_global(cg.RawStatement("#ifndef __PICOLIBC__")) diff --git a/esphome/coroutine.py b/esphome/coroutine.py index f5d512e510..e6632d0c01 100644 --- a/esphome/coroutine.py +++ b/esphome/coroutine.py @@ -63,7 +63,13 @@ class CoroPriority(enum.IntEnum): resolution during code generation. """ - # Platform initialization - must run first + # Logger early init - must run before all other code because: + # 1. All code assumes global_logger is ready for ESP_LOG* calls + # 2. Without this, any log call before logger init dereferences nullptr + # Examples: logger (1100) + LOGGER_INIT = 1100 + + # Platform initialization # Examples: esp32, esp8266, rp2040 PLATFORM = 1000 @@ -83,7 +89,7 @@ class CoroPriority(enum.IntEnum): CORE = 100 # Diagnostic and debugging systems - # Examples: logger (90) + # Examples: debug component (90) DIAGNOSTICS = 90 # Status and monitoring systems diff --git a/esphome/writer.py b/esphome/writer.py index fd4c811fb3..69a35d00e3 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -381,7 +381,10 @@ def write_cpp(code_s): code_format = CPP_BASE_FORMAT copy_src_tree() + # using namespace esphome must precede all variable declarations since + # codegen types assume this namespace is in scope (esphome_ns = global_ns). global_s = '#include "esphome.h"\n' + global_s += "using namespace esphome;\n" global_s += CORE.cpp_global_section full_file = f"{code_format[0] + CPP_INCLUDE_BEGIN}\n{global_s}{CPP_INCLUDE_END}"