curl: (28) Operation timed out after 2003 milliseconds with 0 bytes received
means curl gave up because the whole transfer took longer than the limit you set with
--max-time. Either the server is slower than the limit, or the limit is too
short for that server: raise --max-time above the server's real response time,
or make the server answer faster.
In this test the server took five seconds to answer, the limit was two, and a limit of ten gave a normal answer.
The error
$ time curl -sS --max-time 2 http://web:8081/
curl: (28) Operation timed out after 2002 milliseconds with 0 bytes received
real 0m2.006s
user 0m0.000s
sys 0m0.004s
exit=28
Why it happens
The server on web:8081 was a small Python handler that sleeps five seconds
before it sends its reply. No bytes came back within two seconds
(0 bytes received), so curl stopped at the limit (2002 milliseconds, with time
showing 2.006 s of wall clock) and exited with code 28. The first run printed
2003 milliseconds, so the number moves a little between runs.
--connect-timeout is a different limit. With --connect-timeout 2
and no --max-time, the same slow server did not trigger the error: curl waited
the full five seconds and printed the answer.
$ time curl -sS --connect-timeout 2 http://web:8081/
slow ok
real 0m5.005s
...
exit=0
So in this test --connect-timeout did not limit a server that accepted the
connection and then answered slowly; only --max-time did. A connect timeout
against an address that never answers was not tested here.
The fix
$ time curl -sS --max-time 10 http://web:8081/
slow ok
real 0m5.005s
user 0m0.000s
sys 0m0.005s
exit=0
With a limit above the server's five seconds, the request finished in 5.005 s and printed the body. Pick the limit from the slowest answer you are willing to wait for. If the limit is right and the server is the problem, the fix belongs on the server. One detail of this test: the Python server handled one request at a time, so an earlier timed-out request that was still sleeping on the server made a following request take eight seconds; the captures above come from a rerun with one request at a time.
How it was reproduced
Reproduced inside ubuntu:24.04 containers on one user-defined Docker network on
Docker engine 29.9.3, with curl 8.5.0 from apt in the client. The server container, alias
web, ran a Python 3.12.3 http.server handler on port 8081 that calls
time.sleep(5) before answering. The containers and the network were removed
afterwards.
Frequently asked
- What does curl: (28) Operation timed out mean?
- curl stopped because the transfer took longer than the limit. In this test --max-time 2 against a server that answered after five seconds gave exit code 28.
- What is the difference between curl --max-time and --connect-timeout?
- In this test --max-time 2 stopped a slow answer after two seconds, while --connect-timeout 2 did not: curl waited the full five seconds and printed the answer.
- How do I fix curl error 28?
- Raise --max-time above the server's real response time, or make the server answer faster. Here --max-time 10 returned the answer after five seconds.
If curl cannot connect at all, see curl: (7) Failed to connect. More decoded errors in the Fixes category.