Repository navigation
socket.getaddrinfo() blocks indefinitely when WiFi active but not connected (RP2350/CYW43) #18797
Description
Activity
Thanks for the report. Indeed
socket.getaddrinfo()blocks indefinitely if there's no network up.But, it's not unresponsive. You can break out of the loop using Ctrl-C at the REPL.
We should definitely implement Option A (a timeout), and also probably Option B (fail early when possible).
(We also need an asynchronous DNS interface, eg
aiodns.DNSResolver(), but that's a separate issue.)- addedextmodRelates to extmod/ directory in sourceRelates to extmod/ directory in source
on Feb 10, 2026 - added a commit that references this issue
on Feb 10, 2026 Root Cause Investigation
I investigated this on a Raspberry Pi Pico 2 W (RP2350 + CYW43439) and found the root cause is two factors combining:
1. Cached DNS server addresses
After WiFi disconnect, DNS server addresses from the previous DHCP lease remain in lwIP's state. When
dns_gethostbyname()is called, it sees valid DNS servers and enqueues a query, returningERR_INPROGRESS— even though no network path exists to reach those servers.2. Soft timer self-disables (rp2 port)
At
ports/rp2/mpnetworkport.c:150-161, the lwIP soft timer switches toONE_SHOTmode when no netif hasNETIF_FLAG_LINK_UP:NETIF_FOREACH(netif) { if (netif->flags & NETIF_FLAG_LINK_UP) { keep_running = true; break; } } mp_network_soft_timer.mode = keep_running ? SOFT_TIMER_MODE_PERIODIC : SOFT_TIMER_MODE_ONE_SHOT;
Without the periodic timer,
dns_tmr()never fires, so lwIP's built-in DNS retry/timeout mechanism is dead. The polling loop atextmod/modlwip.c(theERR_INPROGRESScase) spins forever.Why the hang is indefinite (not ~14s)
lwIP's DNS has internal retry logic (4 retries × ~14s total), but that retry logic depends on
dns_tmr()being called periodically. When the soft timer disables itself,dns_tmr()stops, and the timeout counter never advances. The query sits in a DNS slot permanently.Fix
Submitted as #18801. Two changes to the
ERR_INPROGRESScase inextmod/modlwip.c:-
Early-fail: Check
netif_is_up() && netif_is_link_up() && !ip4_addr_isany_val()before polling. If no interface is fully connected, fail immediately with-2. TheLINK_UPcheck is essential because after disconnect, the DHCP IP lingers but link is down. -
Safety-net timeout (20s): Breaks the polling loop if DNS doesn't resolve within 20 seconds, guarding against any other scenario where
dns_tmr()doesn't fire.
Tested all 5 scenarios on Pico 2 W — details in the PR.
-
The unofficial aiodns is a nonblocking DNS client.
The unofficial aiodns is a nonblocking DNS client.
Ah, thanks for the link! That's good to know about.
I've submitted a similar but slightly different fix in #18805 - @paul-matthews if you have a chance to test this out then it'd be much appreciated.
I've submitted a similar but slightly different fix in #18805 - @paul-matthews if you have a chance to test this out then it'd be much appreciated.
Yep, I'll upload to my Pi Pico 2 W and run my repro scripts.
- added a commit that references this issue
on Mar 4, 2026 - added a commit that references this issue
on Mar 7, 2026 - added a commit that references this issue
on Sep 21, 2026
Summary
socket.getaddrinfo()blocks indefinitely on RP2350 (Pico 2 W) when the CYW43 WiFi interface is active (wlan.active(True)) but has no IP address (not yet connected, orconnect()still in progress). No timeout is applied, and the call never returns. The device becomes completely unresponsive — no serial output, no exception, no way to recover except a hard reset.This makes it impossible to write robust networked applications that handle WiFi disconnection gracefully, since any DNS lookup during a WiFi gap will freeze the entire runtime.
Reproduction
Expected behavior
getaddrinfo()should return an error (e.g.,OSErrorwithEAI_FAILor similar) within a reasonable timeout when the network interface has no IP address.Actual behavior
wlan.active(False)):getaddrinfo()fails immediately withOSError: -2— correct behavior.wlan.active(True), noconnect()called):getaddrinfo()blocks indefinitely. The call never returns. No timeout, no exception. The device is completely frozen.Analysis
The issue appears to be in the lwIP DNS resolver. When the WiFi interface is active, lwIP considers the interface "up" and attempts DNS resolution. But with no IP address and no default gateway, the DNS query is sent into the void and lwIP waits for a response that never comes. There is no timeout applied at the
getaddrinfo()level.When the interface is inactive (
wlan.active(False)), lwIP correctly determines there is no available interface and fails fast.Impact
This affects any MicroPython application using
requests.get(),urequests.get(), or rawsocket.getaddrinfo()on Pico W / Pico 2 W during WiFi connection gaps. Therequestsmodule callsgetaddrinfo()beforeconnect()orwrap_socket(), so even applications that guard TLS calls will hang at the DNS stage.The
timeoutparameter passed torequests.get()has no effect becausesocket.settimeout()is not applied until after the socket is created — which happens aftergetaddrinfo().Environment
pimoroni/unicornrepo,micropython/1.26.0branch)Suggested fix
Option A: Apply a timeout to the lwIP DNS query in
socket_getaddrinfo()(extmod/modsocket.c), so it fails withOSErrorafter a bounded time.Option B: Check interface state (has IP address) before initiating DNS resolution and fail immediately if the interface has no network path.