<p dir="auto">When adding an adapter with a retry object to a session, read timeout errors are converted to connection errors.</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; s = requests.Session()
&gt;&gt;&gt; s.get(&quot;http://whatever.com&quot;, timeout=(3.05, 0.001))
Timeout: HTTPConnectionPool(host='whatever.com', port=80): Read timed out. (read timeout=0.001)
&gt;&gt;&gt; a = HTTPAdapter(max_retries=Retry(total=3))
&gt;&gt;&gt; s.mount(&quot;http://&quot;, a) 
&gt;&gt;&gt; s.get(&quot;http://whatever.com&quot;, timeout=(3.05, 0.001))
ConnectionError: HTTPConnectionPool(host='whatever.com', port=80): Max retries exceeded with url: / (Caused by ReadTimeoutError(&quot;HTTPConnectionPool(host='whatever.com', port=80): Read timed out. (read timeout=0.001)&quot;))"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">s</span> <span class="pl-c1">=</span> <span class="pl-s1">requests</span>.<span class="pl-v">Session</span>()
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">s</span>.<span class="pl-en">get</span>(<span class="pl-s">"http://whatever.com"</span>, <span class="pl-s1">timeout</span><span class="pl-c1">=</span>(<span class="pl-c1">3.05</span>, <span class="pl-c1">0.001</span>))
<span class="pl-v">Timeout</span>: <span class="pl-v">HTTPConnectionPool</span>(<span class="pl-s1">host</span><span class="pl-c1">=</span><span class="pl-s">'whatever.com'</span>, <span class="pl-s1">port</span><span class="pl-c1">=</span><span class="pl-c1">80</span>): <span class="pl-v">Read</span> <span class="pl-s1">timed</span> <span class="pl-s1">out</span>. (<span class="pl-s1">read</span> <span class="pl-s1">timeout</span><span class="pl-c1">=</span><span class="pl-c1">0.001</span>)
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">a</span> <span class="pl-c1">=</span> <span class="pl-v">HTTPAdapter</span>(<span class="pl-s1">max_retries</span><span class="pl-c1">=</span><span class="pl-v">Retry</span>(<span class="pl-s1">total</span><span class="pl-c1">=</span><span class="pl-c1">3</span>))
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">s</span>.<span class="pl-en">mount</span>(<span class="pl-s">"http://"</span>, <span class="pl-s1">a</span>) 
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">s</span>.<span class="pl-en">get</span>(<span class="pl-s">"http://whatever.com"</span>, <span class="pl-s1">timeout</span><span class="pl-c1">=</span>(<span class="pl-c1">3.05</span>, <span class="pl-c1">0.001</span>))
<span class="pl-v">ConnectionError</span>: <span class="pl-v">HTTPConnectionPool</span>(<span class="pl-s1">host</span><span class="pl-c1">=</span><span class="pl-s">'whatever.com'</span>, <span class="pl-s1">port</span><span class="pl-c1">=</span><span class="pl-c1">80</span>): <span class="pl-v">Max</span> <span class="pl-s1">retries</span> <span class="pl-s1">exceeded</span> <span class="pl-k">with</span> <span class="pl-s1">url</span>: <span class="pl-c1">/</span> (<span class="pl-v">Caused</span> <span class="pl-s1">by</span> <span class="pl-v">ReadTimeoutError</span>(<span class="pl-s">"HTTPConnectionPool(host='whatever.com', port=80): Read timed out. (read timeout=0.001)"</span>))</pre></div>
<p dir="auto">Is this intentional? If so, what's the reasoning behind it? I'd like to catch these errors, but handle connection errors differently (e.g. warn the user to check his internet connection) from timeout errors (e.g. inform the user that the requested resource is possibly overloaded). Wouldn't it make more sense to raise the original error type once the max number of retries is exceeded?</p>
<p dir="auto">Consider the code below (<strong>main.py</strong>). When a temporary network disconnect occurs without the <code class="notranslate">timeout</code> keyword argument to <code class="notranslate">Session.get()</code>, the client may hang indefinitely and no exception is raised.</p>
<p dir="auto">However, if I use the <code class="notranslate">timeout</code> keyword argument, the application will raise a <code class="notranslate">ConnectionError</code> from <strong>models.py</strong> corresponding to the <code class="notranslate">urllib3.exceptions.ReadTimeoutError</code>:</p>
<p dir="auto"><code class="notranslate">requests.exceptions.ConnectionError: HTTPSConnectionPool(host='confluence.danskenet.net', port=443): Read timed out.</code></p>
<p dir="auto">Given that the exception is only raised, when using the <code class="notranslate">timeout</code> keyword argument, why isn't Requests raising <code class="notranslate">ReadTimeout</code> exception instead? In particular, the <code class="notranslate">ConnectionError</code>'s exception message "Read timed out." suggests that it should be a <code class="notranslate">ReadTimeout</code> exception?</p>
<p dir="auto">To mitigate the issue I'm currently performing a regular expression match on the exception message, which is a bad practice:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="except ConnectionError as e:
     if re.search('Read timed out', str(e), re.IGNORECASE):"><pre class="notranslate"><code class="notranslate">except ConnectionError as e:
     if re.search('Read timed out', str(e), re.IGNORECASE):
</code></pre></div>
<p dir="auto"><strong>main.py</strong>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" try:
     with requests.Session() as rs:
         rs.mount('https://', HTTPAdapter(max_retries=Retry(total=10, connect=10, read=10, backoff_factor=1)))
         with rs.get(url, params={}, headers={}, auth=self.auth, verify=self.ssl_verify, timeout=(30, 30)) as r:
             r.raise_for_status()
             page_set = r.json()
 except ReadTimeout as e:
     logging.exception('Request for page set timed out: {}'.format(url))
     continue
 except ConnectionError as e:
     if re.search('Read timed out', str(e), re.IGNORECASE):
         logging.exception('Request for page set timed out (network problem): {}'.format(url))
         continue
     else:
         raise"><pre class="notranslate"><code class="notranslate"> try:
     with requests.Session() as rs:
         rs.mount('https://', HTTPAdapter(max_retries=Retry(total=10, connect=10, read=10, backoff_factor=1)))
         with rs.get(url, params={}, headers={}, auth=self.auth, verify=self.ssl_verify, timeout=(30, 30)) as r:
             r.raise_for_status()
             page_set = r.json()
 except ReadTimeout as e:
     logging.exception('Request for page set timed out: {}'.format(url))
     continue
 except ConnectionError as e:
     if re.search('Read timed out', str(e), re.IGNORECASE):
         logging.exception('Request for page set timed out (network problem): {}'.format(url))
         continue
     else:
         raise
</code></pre></div>
<p dir="auto"><strong>models.py</strong>:<br>
<a href="https://github.com/psf/requests/blob/master/requests/models.py">https://github.com/psf/requests/blob/master/requests/models.py</a></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def generate():
            # Special case for urllib3.
            if hasattr(self.raw, 'stream'):
                try:
                    for chunk in self.raw.stream(chunk_size, decode_content=True):
                        yield chunk
                except ProtocolError as e:
                    raise ChunkedEncodingError(e)
                except DecodeError as e:
                    raise ContentDecodingError(e)
                except ReadTimeoutError as e:
                    raise ConnectionError(e)"><pre class="notranslate"><code class="notranslate">def generate():
            # Special case for urllib3.
            if hasattr(self.raw, 'stream'):
                try:
                    for chunk in self.raw.stream(chunk_size, decode_content=True):
                        yield chunk
                except ProtocolError as e:
                    raise ChunkedEncodingError(e)
                except DecodeError as e:
                    raise ContentDecodingError(e)
                except ReadTimeoutError as e:
                    raise ConnectionError(e)
</code></pre></div>
<p dir="auto"><strong>Exception</strong>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ERROR -- 04/19/2020 04:51:32 PM -- root -- ThreadPoolExecutor-0_0  -- Request for page set timed out (network problem): https://confluence.danskenet.net/rest/api/content/search?expand=version,history,space,body.storage,children.attachment.version,children.attachment.history,children.attachment.space&amp;limit=50&amp;start=1900&amp;cql=(type=page)
Traceback (most recent call last):
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py&quot;, line 425, in _error_catcher
    yield
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py&quot;, line 755, in read_chunked
    chunk = self._handle_chunk(amt)
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py&quot;, line 708, in _handle_chunk
    returned_chunk = self._fp._safe_read(self.chunk_left)
  File &quot;/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py&quot;, line 620, in _safe_read
    chunk = self.fp.read(min(amt, MAXAMOUNT))
  File &quot;/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py&quot;, line 589, in readinto
    return self._sock.recv_into(b)
  File &quot;/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py&quot;, line 1071, in recv_into
    return self.read(nbytes, buffer)
  File &quot;/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py&quot;, line 929, in read
    return self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/models.py&quot;, line 751, in generate
    for chunk in self.raw.stream(chunk_size, decode_content=True):
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py&quot;, line 560, in stream
    for line in self.read_chunked(amt, decode_content=decode_content):
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py&quot;, line 781, in read_chunked
    self._original_response.close()
  File &quot;/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/contextlib.py&quot;, line 130, in __exit__
    self.gen.throw(type, value, traceback)
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py&quot;, line 430, in _error_catcher
    raise ReadTimeoutError(self._pool, None, &quot;Read timed out.&quot;)
urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='confluence.danskenet.net', port=443): Read timed out.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/confluence/confluence.py&quot;, line 112, in __producer
    with rs.get(url, params={}, headers={}, auth=self.auth, verify=self.ssl_verify, timeout=(30, 30)) as r:
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/sessions.py&quot;, line 543, in get
    return self.request('GET', url, **kwargs)
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/sessions.py&quot;, line 530, in request
    resp = self.send(prep, **send_kwargs)
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/sessions.py&quot;, line 683, in send
    r.content
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/models.py&quot;, line 829, in content
    self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b''
  File &quot;/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/models.py&quot;, line 758, in generate
    raise ConnectionError(e)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='confluence.danskenet.net', port=443): Read timed out."><pre class="notranslate"><code class="notranslate">ERROR -- 04/19/2020 04:51:32 PM -- root -- ThreadPoolExecutor-0_0  -- Request for page set timed out (network problem): https://confluence.danskenet.net/rest/api/content/search?expand=version,history,space,body.storage,children.attachment.version,children.attachment.history,children.attachment.space&amp;limit=50&amp;start=1900&amp;cql=(type=page)
Traceback (most recent call last):
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py", line 425, in _error_catcher
    yield
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py", line 755, in read_chunked
    chunk = self._handle_chunk(amt)
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py", line 708, in _handle_chunk
    returned_chunk = self._fp._safe_read(self.chunk_left)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 620, in _safe_read
    chunk = self.fp.read(min(amt, MAXAMOUNT))
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py", line 589, in readinto
    return self._sock.recv_into(b)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 1071, in recv_into
    return self.read(nbytes, buffer)
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 929, in read
    return self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/models.py", line 751, in generate
    for chunk in self.raw.stream(chunk_size, decode_content=True):
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py", line 560, in stream
    for line in self.read_chunked(amt, decode_content=decode_content):
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py", line 781, in read_chunked
    self._original_response.close()
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/contextlib.py", line 130, in __exit__
    self.gen.throw(type, value, traceback)
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/urllib3/response.py", line 430, in _error_catcher
    raise ReadTimeoutError(self._pool, None, "Read timed out.")
urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='confluence.danskenet.net', port=443): Read timed out.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/nlykkei/projects/atlassian-watchdog/confluence/confluence.py", line 112, in __producer
    with rs.get(url, params={}, headers={}, auth=self.auth, verify=self.ssl_verify, timeout=(30, 30)) as r:
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/sessions.py", line 543, in get
    return self.request('GET', url, **kwargs)
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/sessions.py", line 530, in request
    resp = self.send(prep, **send_kwargs)
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/sessions.py", line 683, in send
    r.content
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/models.py", line 829, in content
    self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b''
  File "/Users/nlykkei/projects/atlassian-watchdog/lib/python3.7/site-packages/requests/models.py", line 758, in generate
    raise ConnectionError(e)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='confluence.danskenet.net', port=443): Read timed out.
</code></pre></div>
<h2 dir="auto">Expected Result</h2>
<p dir="auto">I would expect a <code class="notranslate">requests.exceptions.ReadTimeout</code> to be raised.</p>
<h2 dir="auto">Actual Result</h2>
<p dir="auto">A <code class="notranslate">requests.exceptions.ConnectError</code> was raised instead, with the error message: <code class="notranslate">Read timed out.</code></p>
<h2 dir="auto">System Information</h2>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ python -m requests.help"><pre class="notranslate"><code class="notranslate">$ python -m requests.help
</code></pre></div>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="nlykkei:~$ python3 -m requests.help
{
  &quot;chardet&quot;: {
    &quot;version&quot;: &quot;3.0.4&quot;
  },
  &quot;cryptography&quot;: {
    &quot;version&quot;: &quot;&quot;
  },
  &quot;idna&quot;: {
    &quot;version&quot;: &quot;2.7&quot;
  },
  &quot;implementation&quot;: {
    &quot;name&quot;: &quot;CPython&quot;,
    &quot;version&quot;: &quot;3.7.7&quot;
  },
  &quot;platform&quot;: {
    &quot;release&quot;: &quot;19.4.0&quot;,
    &quot;system&quot;: &quot;Darwin&quot;
  },
  &quot;pyOpenSSL&quot;: {
    &quot;openssl_version&quot;: &quot;&quot;,
    &quot;version&quot;: null
  },
  &quot;requests&quot;: {
    &quot;version&quot;: &quot;2.23.0&quot;
  },
  &quot;system_ssl&quot;: {
    &quot;version&quot;: &quot;1010106f&quot;
  },
  &quot;urllib3&quot;: {
    &quot;version&quot;: &quot;1.24.3&quot;
  },
  &quot;using_pyopenssl&quot;: false
}"><pre class="notranslate"><code class="notranslate">nlykkei:~$ python3 -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": ""
  },
  "idna": {
    "version": "2.7"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.7.7"
  },
  "platform": {
    "release": "19.4.0",
    "system": "Darwin"
  },
  "pyOpenSSL": {
    "openssl_version": "",
    "version": null
  },
  "requests": {
    "version": "2.23.0"
  },
  "system_ssl": {
    "version": "1010106f"
  },
  "urllib3": {
    "version": "1.24.3"
  },
  "using_pyopenssl": false
}
</code></pre></div>
<p dir="auto">This command is only available on Requests v2.16.4 and greater. Otherwise,<br>
please provide some basic information about your system (Python version,<br>
operating system, &amp;c).</p>