Repository navigation
socket.getaddrinfo() blocks indefinitely when WiFi active but not connected (RP2350/CYW43) #18797
Description
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, or connect() 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
# repro_getaddrinfo_hang.py — Raspberry Pi Pico 2 W
# Demonstrates that getaddrinfo() hangs when WiFi is active but not connected.
# Run immediately after boot (before WiFi has connected).
import network
import socket
import time
def run_test():
wlan = network.WLAN(network.STA_IF)
# Test 1: WiFi not activated — getaddrinfo fails fast
wlan.active(False)
time.sleep(0.5)
print(f"Test 1: active={wlan.active()}, connected={wlan.isconnected()}")
try:
print(" Calling getaddrinfo()...")
ai = socket.getaddrinfo("example.com", 443, 0, socket.SOCK_STREAM)
print(f" Result: {ai[0][-1]}")
except OSError as e:
print(f" OSError: {e}") # Expected: fails fast with error code -2
# Test 2: WiFi activated, not connected — getaddrinfo HANGS
wlan.active(True)
time.sleep(1)
print(f"\nTest 2: active={wlan.active()}, connected={wlan.isconnected()}")
print(" Calling getaddrinfo()...")
print(" >>> If output stops here, getaddrinfo() is blocking indefinitely <<<")
try:
ai = socket.getaddrinfo("example.com", 443, 0, socket.SOCK_STREAM)
print(f" Result: {ai[0][-1]}")
except OSError as e:
print(f" OSError: {e}")
print("\nBoth tests completed without hanging.")
run_test()Expected behavior
getaddrinfo() should return an error (e.g., OSError with EAI_FAIL or similar) within a reasonable timeout when the network interface has no IP address.
Actual behavior
- WiFi not active (
wlan.active(False)):getaddrinfo()fails immediately withOSError: -2— correct behavior. - WiFi active, not connected (
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 raw socket.getaddrinfo() on Pico W / Pico 2 W during WiFi connection gaps. The requests module calls getaddrinfo() before connect() or wrap_socket(), so even applications that guard TLS calls will hang at the DNS stage.
The timeout parameter passed to requests.get() has no effect because socket.settimeout() is not applied until after the socket is created — which happens after getaddrinfo().
Environment
- Board: Raspberry Pi Pico 2 W (RP2350 + CYW43)
- MicroPython: v1.26.1
- Pico SDK: 2.1.1
- Build: Pimoroni Cosmic Unicorn board variant (custom build from
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 with OSError after 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.
Activity
dpgeorge commentedon Feb 10, 2026
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.)extmod/modlwip: Fix getaddrinfo blocking indefinitely without network.
paul-matthews commentedon 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: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.
peterhinch commentedon Feb 10, 2026
The unofficial aiodns is a nonblocking DNS client.
dpgeorge commentedon Feb 11, 2026
Ah, thanks for the link! That's good to know about.
projectgus commentedon Feb 17, 2026
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.
paul-matthews commentedon Feb 22, 2026
Yep, I'll upload to my Pi Pico 2 W and run my repro scripts.
extmod,rp2: Keep LWIP timer running if lwip poll_sockets() is called.
extmod,rp2: Keep LWIP timer running if lwip poll_sockets() is called.
extmod,rp2: Keep LWIP timer running if lwip poll_sockets() is called.