psf/requests · error · OSError
Could not find the TLS key file, invalid path: {conn.key_fil
Error message
Could not find the TLS key file, invalid path: {conn.key_file} What it means
The companion check to error 22: when `cert` is given as a (cert_file, key_file) tuple, HTTPAdapter.cert_verify verifies the private key path exists and raises this OSError if it doesn't. It only fires for the tuple form, since the string form sets key_file to None (key assumed to be in the same PEM).
Source
Thrown at src/requests/adapters.py:361
else:
conn.cert_reqs = "CERT_NONE"
conn.ca_certs = None
conn.ca_cert_dir = None
if cert:
if not isinstance(cert, basestring):
conn.cert_file = cert[0]
conn.key_file = cert[1]
else:
conn.cert_file = cert
conn.key_file = None
if conn.cert_file and not os.path.exists(conn.cert_file):
raise OSError(
f"Could not find the TLS certificate file, "
f"invalid path: {conn.cert_file}"
)
if conn.key_file and not os.path.exists(conn.key_file):
raise OSError(
f"Could not find the TLS key file, invalid path: {conn.key_file}"
)
def build_response(self, req: PreparedRequest, resp: Any) -> Response:
"""Builds a :class:`Response <requests.Response>` object from a urllib3
response. This should not be called from user code, and is only exposed
for use when subclassing the
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
:param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
:param resp: The urllib3 response object.
:rtype: requests.Response
"""
assert _is_prepared(req)
response = Response()
# Fallback to None if there's no status_code, for whatever reason.
response.status_code = getattr(resp, "status", None) # type: ignore[assignment]View on GitHub ↗ (pinned to 414f0513c3)
Solutions
- Confirm the key file exists at the given absolute path and the tuple order is (cert, key)
- Provision the private key to the runtime environment (secret mount, deploy step) — it's often intentionally absent from the artifact
- If cert and key are combined in one PEM, pass that single path as a string instead of a tuple
Example fix
# before
requests.get(url, cert=('client.crt', 'client.key')) # key never deployed
# after — combined PEM shipped via secret mount
requests.get(url, cert='/run/secrets/client-combined.pem') Defensive patterns
Strategy: validation
Validate before calling
import os
if isinstance(cert, tuple):
key_file = cert[1]
if not os.path.isfile(key_file):
raise FileNotFoundError(f"Client TLS key not found: {key_file}") Try / catch
try:
resp = session.get(url, cert=(cert_file, key_file))
except OSError as e:
raise RuntimeError(f"Client key path invalid: {key_file}") from e Prevention
- Validate both members of the (cert, key) tuple together before any request
- Keep cert and key deployment in one step so they cannot drift apart
- Fail fast at startup rather than on the first TLS request
When it happens
Trigger: requests.get(url, cert=('client.crt','client.key')) where the key path is missing, misspelled, or unreadable-by-path; swapping tuple elements so the 'key' slot holds a nonexistent path. Raised at src/requests/adapters.py:360-363.
Common situations: Private keys deliberately excluded from repos/images (correctly gitignored) and never provisioned to the runtime; key files with restrictive locations (/etc/ssl/private) referenced from a non-root process using a wrong path; cert renewal tooling writing the new key under a different filename.
Related errors
- Could not find the TLS certificate file, invalid path: {conn
- Could not find a suitable TLS CA certificate bundle, invalid
- Invalid URL {url!r}: No host supplied
- Please check proxy URL. It is malformed and could be missing
- Invalid timeout {timeout}. Pass a (connect, read) timeout tu
AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31).
Data as JSON: /data/errors/b4a8a0373582a990.json.
Report an issue: GitHub ↗.