Problem
Wlan::addCredentials() is called from the async_tcp context via WebHandler.cpp:703 (setNetwork) and WebHandler.cpp:732 (addNetwork). Inside addCredentials(), it directly calls WiFi.disconnect() and WiFi.begin() — both are blocking operations that acquire internal WiFi mutexes via xSemaphoreTake(portMAX_DELAY), blocking the async_tcp task for ~100–300 ms.
In combination with the previously fixed CR-003-001 (NVS write from async_tcp), the addNetwork path (force=true) could block async_tcp for >500 ms total, causing the browser to receive the response too late — especially critical in the Captive Portal flow where iOS/Android heuristics may drop the connection.
Additionally, the wifiState = WifiState::AddCredentials assignment in addCredentials() created a race condition between async_tcp and ConnectTask (CR-003-006).
Root Cause
The Pending-Flag pattern (established in B39-fix: wifiModePsPending, wlanSaveConfigPending, recoveryPending) was not applied to the WiFi connect initiation path. WiFi-API calls in the async_tcp handler context are forbidden in ESP-IDF 4.x (CLAUDE.md convention).
Fix
Introduced newCredentialsPending flag (static member). addCredentials() now only stores credentials and sets the flag — no WiFi calls. In update() (ConnectTask context), the pending block performs WiFi.disconnect() + WiFi.begin() + esp_wifi_set_config() safely. Also added return-value check on esp_wifi_get_config() to prevent writing uninitialized stack data on failure.
This also resolves the wifiState write race (CR-003-006 partial fix) since wifiState = AddCredentials now only happens in ConnectTask.
Review-Finding: CR-003-003
Severity: HIGH
Problem
Wlan::addCredentials()is called from the async_tcp context viaWebHandler.cpp:703(setNetwork) andWebHandler.cpp:732(addNetwork). InsideaddCredentials(), it directly callsWiFi.disconnect()andWiFi.begin()— both are blocking operations that acquire internal WiFi mutexes viaxSemaphoreTake(portMAX_DELAY), blocking the async_tcp task for ~100–300 ms.In combination with the previously fixed CR-003-001 (NVS write from async_tcp), the
addNetworkpath (force=true) could block async_tcp for >500 ms total, causing the browser to receive the response too late — especially critical in the Captive Portal flow where iOS/Android heuristics may drop the connection.Additionally, the
wifiState = WifiState::AddCredentialsassignment inaddCredentials()created a race condition between async_tcp and ConnectTask (CR-003-006).Root Cause
The Pending-Flag pattern (established in B39-fix:
wifiModePsPending,wlanSaveConfigPending,recoveryPending) was not applied to the WiFi connect initiation path. WiFi-API calls in the async_tcp handler context are forbidden in ESP-IDF 4.x (CLAUDE.md convention).Fix
Introduced
newCredentialsPendingflag (static member).addCredentials()now only stores credentials and sets the flag — no WiFi calls. Inupdate()(ConnectTask context), the pending block performsWiFi.disconnect()+WiFi.begin()+esp_wifi_set_config()safely. Also added return-value check onesp_wifi_get_config()to prevent writing uninitialized stack data on failure.This also resolves the
wifiStatewrite race (CR-003-006 partial fix) sincewifiState = AddCredentialsnow only happens in ConnectTask.Review-Finding: CR-003-003
Severity: HIGH