requests: guard against getaddrinfo() hang when WiFi not connected
Summary
The request() function in requests/__init__.py calls socket.getaddrinfo() (line 81) without verifying network connectivity. On platforms where getaddrinfo() blocks indefinitely when the WiFi interface is active but not connected (see micropython/micropython#18797), this causes the device to freeze with no way to recover except a hard reset.
The timeout parameter passed to requests.get() has no effect because socket.settimeout() is applied to the socket object (line 93) — which is created after getaddrinfo() returns. So the hang occurs before any timeout takes effect.
Suggested fix
Add a connectivity check before getaddrinfo():
# Guard: getaddrinfo() blocks indefinitely on RP2040/RP2350 when the
# CYW43 WiFi interface is active but has no IP address.
try:
import network
_wlan = network.WLAN(network.STA_IF)
if not _wlan.isconnected():
raise OSError(-1, "WiFi not connected")
except ImportError:
pass # Non-WiFi platform, skip guard
ai = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
This is wrapped in try/except ImportError so it's safe on non-WiFi platforms (ESP32 Ethernet, Unix port, etc.).
Also consider
A default socket timeout (e.g., 30s) applied before getaddrinfo() would provide defense-in-depth, though it wouldn't help here since settimeout() only affects socket operations, not DNS resolution.
Environment
- Board: Raspberry Pi Pico 2 W (RP2350 + CYW43)
- MicroPython: v1.26.1
- Upstream issue: micropython/micropython#18797
socket.getaddrinfo() blocks indefinitely when WiFi active but not connected (RP2350/CYW43)
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.
This needs to be fixed properly in
socket.getaddrinfo()so it doesn't block indefinitely.