Disabling AutoAP with no SSID configured causes a boot loop (lwip assert in WEB_Utils::setup) #285

Closed
opened 2026-10-05 05:57:21 +02:00 by adam · 3 comments
Owner

Summary

Disabling the AutoAP fallback while no WiFi SSID is configured leaves the device
with no network interface at all. WEB_Utils::setup() still starts the web
server, and lwip aborts. The device then reboots in a loop and can only be
recovered over USB.

On a remote or hard-to-reach installation (a hilltop digipeater, for example)
this means physically retrieving the device.

Reproduction

  1. Leave the WiFi SSID empty (normal for a digipeater with no network on site).
  2. Uncheck AutoAP in the web configuration — a natural thing to do to save power
    on a solar-powered site.
  3. Reboot.

Serial output

SPIFFS Mounted
Reading config..
Config read successfuly
[NM] Initializing Networking...

Starting Station: <CALL>   Version: 2026-04-21
(DigiEcoMode: OFF)
init : LoRa Module    ...     done!
WiFi SSID not set!

assert failed: tcpip_api_call IDF/components/lwip/lwip/src/api/tcpip.c:497 (Invalid mbox)

Backtrace: 0x40083e2d:0x3ffcd1c0 0x40094df5:0x3ffcd1e0 0x4009a765:0x3ffcd200 ...

Rebooting...

…and the same sequence repeats indefinitely.

Cause

In WIFI_Utils::startWiFi(), with no SSID and AutoAP disabled, neither STA nor AP
is started:

if (!networkManager->hasWiFiNetworks()) {
    Serial.println("WiFi SSID not set!");
    if (Config.wifiAutoAP.enabled) {
        Serial.println("Starting AP fallback...");
        startAutoAP();
    }
    return;
}

setup() then continues with the network-dependent modules. Three of them guard
on connectivity, one does not:

module guard
NTP_Utils::setup() networkManager->isConnected() && ecoMode == 0
SYSLOG_Utils::setup() networkManager->isConnected()
WX_Utils::setup() wxsensor.active (not network)
WEB_Utils::setup() ecoMode == 0 only

So WEB_Utils::setup() registers its handlers, calls OTA_Utils::setup(&server)
and then server.begin() with no netif initialised — hence the lwip assert.

This also explains why ecoMode = 2 (WiFi off) does not crash: the same
ecoMode == 0 condition skips the web server. Only the autoAP = false +
ecoMode = 0 combination reaches server.begin() without a network.

Suggested fix

Align the guard with the other three modules:

void setup() {
    if (Config.digi.ecoMode == 0 && networkManager->isConnected()) {
        ...
        server.begin();
    }
}

A second safeguard worth considering: refuse to disable AutoAP from the web form
when no SSID is set, since that combination leaves no way back in.

Tested on a TTGO LoRa32 v2.1.6, V3.2.4.

## Summary Disabling the AutoAP fallback while no WiFi SSID is configured leaves the device with **no network interface at all**. `WEB_Utils::setup()` still starts the web server, and lwip aborts. The device then reboots in a loop and can only be recovered over USB. On a remote or hard-to-reach installation (a hilltop digipeater, for example) this means physically retrieving the device. ## Reproduction 1. Leave the WiFi SSID empty (normal for a digipeater with no network on site). 2. Uncheck AutoAP in the web configuration — a natural thing to do to save power on a solar-powered site. 3. Reboot. ## Serial output ``` SPIFFS Mounted Reading config.. Config read successfuly [NM] Initializing Networking... Starting Station: <CALL> Version: 2026-04-21 (DigiEcoMode: OFF) init : LoRa Module ... done! WiFi SSID not set! assert failed: tcpip_api_call IDF/components/lwip/lwip/src/api/tcpip.c:497 (Invalid mbox) Backtrace: 0x40083e2d:0x3ffcd1c0 0x40094df5:0x3ffcd1e0 0x4009a765:0x3ffcd200 ... Rebooting... ``` …and the same sequence repeats indefinitely. ## Cause In `WIFI_Utils::startWiFi()`, with no SSID and AutoAP disabled, neither STA nor AP is started: ```cpp if (!networkManager->hasWiFiNetworks()) { Serial.println("WiFi SSID not set!"); if (Config.wifiAutoAP.enabled) { Serial.println("Starting AP fallback..."); startAutoAP(); } return; } ``` `setup()` then continues with the network-dependent modules. Three of them guard on connectivity, one does not: | module | guard | |---|---| | `NTP_Utils::setup()` | `networkManager->isConnected() && ecoMode == 0` | | `SYSLOG_Utils::setup()` | `networkManager->isConnected()` | | `WX_Utils::setup()` | `wxsensor.active` (not network) | | **`WEB_Utils::setup()`** | **`ecoMode == 0` only** | So `WEB_Utils::setup()` registers its handlers, calls `OTA_Utils::setup(&server)` and then `server.begin()` with no netif initialised — hence the lwip assert. This also explains why `ecoMode = 2` (WiFi off) does *not* crash: the same `ecoMode == 0` condition skips the web server. Only the `autoAP = false` + `ecoMode = 0` combination reaches `server.begin()` without a network. ## Suggested fix Align the guard with the other three modules: ```cpp void setup() { if (Config.digi.ecoMode == 0 && networkManager->isConnected()) { ... server.begin(); } } ``` A second safeguard worth considering: refuse to disable AutoAP from the web form when no SSID is set, since that combination leaves no way back in. Tested on a TTGO LoRa32 v2.1.6, V3.2.4. <!-- github-sync-id:4949737265 -->
adam closed this issue 2026-10-05 05:57:21 +02:00
Author
Owner

nice findings.... should this fix just be added on web_utils.cpp or elsewhere?

will check this out also as a validation of both ways you mentioned

nice findings.... should this fix just be added on web_utils.cpp or elsewhere? will check this out also as a validation of both ways you mentioned <!-- github-comment-id:5050421989 -->
Author
Owner

Where the #445 fix belongs

Both places, for two different reasons.

web_utils.cpp — setup() calls server.begin() without checking that a
network interface exists. A guard there stops the lwip assert. That fixes the
crash.

network_manager.cpp — that fixes the actual problem. A unit with no valid
SSID and no AP is unreachable: no configuration page, no way back without
reflashing. That is what happened to me. The AP should start unconditionally when
no usable SSID is configured, whatever the autoAP setting says.

The first prevents the panic, the second prevents a configuration mistake from
turning into a dead device.

## Where the #445 fix belongs Both places, for two different reasons. **`web_utils.cpp`** — `setup()` calls `server.begin()` without checking that a network interface exists. A guard there stops the lwip assert. That fixes the crash. **`network_manager.cpp`** — that fixes the actual problem. A unit with no valid SSID and no AP is unreachable: no configuration page, no way back without reflashing. That is what happened to me. The AP should start unconditionally when no usable SSID is configured, whatever the `autoAP` setting says. The first prevents the panic, the second prevents a configuration mistake from turning into a dead device. <!-- github-comment-id:5056033456 -->
Author
Owner

this has been solved for V4.0.1 please test as it will be available soon (had to do a few changes though)

this has been solved for V4.0.1 please test as it will be available soon (had to do a few changes though) <!-- github-comment-id:5797851400 -->
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adam/LoRa_APRS_iGate#285