From 1b90ccde27407f01051c88e14c114cf6dfa7e987 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 4 Feb 2026 21:14:25 +0100 Subject: [PATCH] [web_server] Fix ESP8266 watchdog panic by deferring actions to main loop --- esphome/components/web_server/web_server.cpp | 15 +-------------- esphome/components/web_server/web_server.h | 11 +++++------ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index d30cb524f4..a3bf1e6b02 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -721,11 +721,7 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM } if (action != SWITCH_ACTION_NONE) { -#ifdef USE_ESP8266 - execute_switch_action(obj, action); -#else this->defer([obj, action]() { execute_switch_action(obj, action); }); -#endif request->send(200); } else { request->send(404); @@ -1645,11 +1641,7 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat } if (action != LOCK_ACTION_NONE) { -#ifdef USE_ESP8266 - execute_lock_action(obj, action); -#else this->defer([obj, action]() { execute_lock_action(obj, action); }); -#endif request->send(200); } else { request->send(404); @@ -2015,19 +2007,14 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur return; } -#ifdef USE_ESP8266 - // ESP8266 is single-threaded, call directly - call.set_raw_timings_base64url(encoded); - call.perform(); -#else // Defer to main loop for thread safety. Move encoded string into lambda to ensure // it outlives the call - set_raw_timings_base64url stores a pointer, so the string // must remain valid until perform() completes. + // ESP8266 also needs this because ESPAsyncWebServer callbacks run in "sys" context. this->defer([call, encoded = std::move(encoded)]() mutable { call.set_raw_timings_base64url(encoded); call.perform(); }); -#endif request->send(200); return; diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 92a5c7edee..224c051ece 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -42,13 +42,12 @@ using ParamNameType = const __FlashStringHelper *; using ParamNameType = const char *; #endif -// ESP8266 is single-threaded, so actions can execute directly in request context. -// Multi-core platforms need to defer to main loop thread for thread safety. -#ifdef USE_ESP8266 -#define DEFER_ACTION(capture, action) action -#else +// All platforms need to defer actions to main loop thread. +// Multi-core platforms need this for thread safety. +// ESP8266 needs this because ESPAsyncWebServer callbacks run in "sys" context +// (SDK system context), not "cont" context (continuation/main loop). Calling +// yield() from sys context causes a panic in the Arduino core. #define DEFER_ACTION(capture, action) this->defer([capture]() mutable { action; }) -#endif /// Result of matching a URL against an entity struct EntityMatchResult {