pythonpython-requests

Python API call freezes instead of raising TimeoutError despite short timeout


I have a very sensitive production script that I’ve been running consistently for the past 4 years. This specific function (runAPI) is called about 100 times every day and has never caused any issues — until today.

I just want the function to exit, if request fails WITHOUT CRASHING/STOPPING THE WHOLE PROGRAM

Today, my code froze while executing the runAPI() function. It did not proceed to any lines of code after the function call, and I was unable to exit from it. This is surprising because I've set a timeout=2 in the requests call, and I expected it to fail gracefully in at most 2 seconds if there was an issue.

Here are the two possible changes that might have triggered this:

I recently switched my GCP virtual machine. However, I reinstalled all libraries and dependencies in the new server.

The target API server might be experiencing issues (I'm currently unable to reach their support).

Both of these possibilities seem unlikely to cause such a complete freeze, so I'm unsure what's going wrong.

Here’s the relevant code snippet:

import requests
import json

def runAPI():
orderParams = {
    "someparameterhere": "example",
    "moreparameterhere": "example"
}

headerData = {
    'Content-type': 'application/json',
    'X-ClientLocalIP': '11.161.0.23',
    'X-ClientPublicIP': '11.22.55.66',
    'X-MACAddress': '32:01:0a:a0:00:16',
    'Accept': 'application/json',
    'X-PrivateKey': 'privatekeyhere',
    'X-UserType': 'USER',
    'X-SourceID': 'WEB',
    'Authorization': 'Bearer somelongstringhere.'
}

for j in range(1):
    try:
        requestid = requests.request(
            "POST",
            "https://apiurl.com/apidataurlexample",
            data=json.dumps(orderParams),
            headers=headerData,
            timeout=2  # expecting this to limit the freeze to 2 seconds
        ).json()
        return requestid
    except Exception as e:
        print(e)
return 0

Here’s the traceback I received:

httplib_response = conn.getresponse()
                     ^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/http/client.py", line 1374, in getresponse
  response.begin()
File "/usr/lib/python3.11/http/client.py", line 318, in begin
  version, status, reason = self._read_status()
                            ^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/http/client.py", line 279, in _read_status
  line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/socket.py", line 706, in readinto
  return self._sock.recv_into(b)
         ^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/ssl.py", line 1311, in recv_into
  return self.read(nbytes, buffer)
         ^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/ssl.py", line 1167, in read
  return self._sslobj.read(len, buffer)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TimeoutError: The read operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/usernamehere/venv3/lib/python3.11/site-packages/requests/adapters.py", line 440, in send
  resp = conn.urlopen(
         ^^^^^^^^^^^^^
File "/home/usernamehere/venv3/lib/python3.11/site-packages/urllib3/connectionpool.py", line 802, in urlopen

My questions:

Why is my function freezing if a timeout is explicitly defined?

Shouldn’t requests raise an exception or return after 2 seconds?

Could the new GCP server environment be responsible?

Is there a better way to enforce strict timeouts and prevent total freezes?

Any insights would be greatly appreciated.

I am unable to reproduce it, because I cannot run the code again today, since it is production code. Tomorrow, I will retry, but if the same error happens again, I am in BIG BIG TROUBLE. So I do not want this error to happen again!!

Edit: I am still getting some failed response from the API website like 10% of the time, the error says "Connection reset by peer". But this error is getting caught by my exception and it does not crash the program.

Edit: I am using Thread() to call this function like 20 times at once. Could this be the issue? My random guess is. One reason for this error could be that. The new GCP VM is very fast. So it might be able to run all the threads at once, and maybe two threads gave the same error at once, so it made the program to crash? Maybe there should be a gap of 0.00001 second between two thread error exceptions????


Solution

  • This is different "timeout", by setting timeout=2 you only handle requests library timeout (requests.exceptions.Timeout), but TimeoutError: The read operation timed out is still not handled (it happens in libraries below requests).

    Try this code:

    requestid = requests.request(
                "POST",
                "https://apiurl.com/apidataurlexample",
                data=json.dumps(orderParams),
                headers=headerData,
                timeout=(2, 2)  # expecting this to limit the freeze to 2 seconds
            ).json()
    

    From docs:

    :param timeout: (optional) How many seconds to wait for the server to send data
        before giving up, as a float, or a :ref:`(connect timeout, read
        timeout) <timeouts>` tuple.