When timeout_ms is not explicitly configured (the default), the SDK passes timeout=None to httpx.Client.build_request(), which overrides httpx's built-in Timeout(5.0) default and disables all timeouts for the request. This causes indefinite hangs when TCP connections stall during response body reads.
Root cause
basesdk.py:212:
timeout = timeout_ms / 1000 if timeout_ms is not None else None
This None is then passed to:
client.build_request(
...,
timeout=timeout, # None → disables httpx timeout entirely
)
In httpx, an explicit timeout=None means "no timeout" — it does not mean "use the client default." The correct sentinel for "no override" is httpx.USE_CLIENT_DEFAULT.
Impact
sdk_configuration.timeout_ms defaults to None
- Every request made without an explicit
timeout_ms has no read timeout
- If a TCP connection stalls (e.g., CloudFlare → provider network issue), the request blocks forever
- We've reproduced this at ~5% rate against
nvidia/nemotron-3-super-120b-a12b via DeepInfra — requests hang indefinitely during receive_response_body while OpenRouter logs show the generation completed normally
Reproduction
import openrouter
sdk = openrouter.OpenRouter(api_key="...")
print(sdk.sdk_configuration.timeout_ms) # None
# This request has NO timeout — will hang forever on TCP stall
response = sdk.chat.send(messages=[...], model="nvidia/nemotron-3-super-120b-a12b")
When
timeout_msis not explicitly configured (the default), the SDK passestimeout=Nonetohttpx.Client.build_request(), which overrides httpx's built-inTimeout(5.0)default and disables all timeouts for the request. This causes indefinite hangs when TCP connections stall during response body reads.Root cause
basesdk.py:212:This
Noneis then passed to:In httpx, an explicit
timeout=Nonemeans "no timeout" — it does not mean "use the client default." The correct sentinel for "no override" ishttpx.USE_CLIENT_DEFAULT.Impact
sdk_configuration.timeout_msdefaults toNonetimeout_mshas no read timeoutnvidia/nemotron-3-super-120b-a12bvia DeepInfra — requests hang indefinitely duringreceive_response_bodywhile OpenRouter logs show the generation completed normallyReproduction