Code review example: requests.get with no timeout can hang the handler forever in Python

Retired from the DiffDojo practice pool in October 2026; the review below is the canonical answer. An AI wrote this pull request for the task underneath. Read the diff and decide what you would say before you scroll to the review.

Python · FastAPI · Edge cases & error handling · beginner

The task

Fetch the exchange rate

  1. get_rate(base, quote) fetches the FX rate from the upstream provider.
  2. The upstream is a third-party HTTP API that can be slow or hang.
  3. If the upstream stops sending data, the call must time out rather than wait indefinitely.

Adds the FX rate fetch: calls the upstream provider and returns the parsed rate using requests.

get_rate runs inside a request handler that serves the checkout page; the upstream provider occasionally stalls.

The diff

app/fx/rates.py+9
1+ import requests
2+
3+ PROVIDER = "https://fx.example.com/rate"
4+
5+
6+ def get_rate(base: str, quote: str) -> float:
7+ resp = requests.get(PROVIDER, params={"base": base, "quote": quote})
8+ resp.raise_for_status()
9+ return resp.json()["rate"]

The review

Issue 1: requests.get with no timeout can hang the handler forever

app/fx/rates.py, line 7

  1. requests has no default timeout. Without a timeout=, requests.get waits as long as the socket keeps the connection open, indefinitely if the upstream accepts the connection and then stops sending bytes.
  2. The context makes that fatal. This runs inside the checkout request handler against a provider that "occasionally stalls". One hung upstream call pins that worker; a few of them exhaust the pool and checkout stops responding for everyone. Requirement 3 asked for exactly the opposite.
  3. It never shows up in testing. Against a fast local stub the call returns instantly; the missing timeout only bites when the real upstream misbehaves.

The fix: pass an explicit timeout= (a socket-inactivity deadline, not a hard total). Where a stall should degrade gracefully, catch requests.Timeout at the appropriate caller boundary.

The fix

import requests

PROVIDER = "https://fx.example.com/rate"


def get_rate(base: str, quote: str) -> float:
    resp = requests.get(
        PROVIDER, params={"base": base, "quote": quote}, timeout=3
    )
    resp.raise_for_status()
    return resp.json()["rate"]

Why it matters

Every outbound HTTP call needs an explicit timeout. requests has *no* default timeout; other clients vary (HTTPX defaults to a 5s inactivity timeout), so whatever you use, verify and configure its timeout policy, a dependency that hangs must not become a dependency that hangs *you*.

Real-world stakes: a third party has a bad afternoon, your workers all block on it waiting for a response that never comes, and your own site goes down even though nothing in your code is broken, a cascading outage from one missing keyword argument. Omitted timeouts are one of the most common resilience bugs, and assistants leave them off because the happy-path call works without one.

Read more: python requests default timeout, requests.get timeout argument, http client hang no timeout

The verdict

Request changes. requests.get with no timeout can hang the handler forever

Try a live one

Python PRs you have not seen the answer to.

Leave inline comments and a verdict, then see what you caught. Free, no signup.

Review a Python PR →

How to review Python code · All code review examples