psf/requests · error · OSError
Could not find the TLS key file, invalid path
Error message
Could not find the TLS key file, invalid path: {conn.key_file} What it means
Raised during cert_verify when a client certificate is supplied as a (cert, key) tuple and the resolved private key file path does not exist. After setting conn.key_file from the second tuple element, the code checks os.path.exists(conn.key_file); a missing key file aborts before the TLS handshake. The single-string cert form sets conn.key_file=None and so never triggers this branch.
Solutions
- Verify the key path exists with os.path.exists and that it is the second element of the cert tuple.
- Check file permissions/ownership for the process running the request.
- Ensure the secret volume mounts both files at the expected paths.
- If cert and key are combined in one PEM, pass the single string form instead of a tuple.
- In deployment, validate both halves of the cert pair in a startup probe.
Example fix
# before
session.cert = ("/etc/secrets/client.crt", "/etc/secrets/client.key") # key missing -> OSError
# after
import os
crt, key = "/etc/secrets/client.crt", "/etc/secrets/client.key"
assert os.path.exists(crt) and os.path.exists(key), "cert or key missing"
session.cert = (crt, key) Defensive patterns
Strategy: validation
Validate before calling
import os
def resolve_client_cert_tuple(cert):
"""For the (cert, key) tuple form, validate both files exist."""
if isinstance(cert, (tuple, list)):
crt, key = cert
if not (os.path.exists(crt) and os.path.exists(key)):
raise FileNotFoundError(
f"cert/key missing: cert={crt} key={key}"
)
return (crt, key)
return cert # string or None handled elsewhere
session.cert = resolve_client_cert_tuple(config["client_cert"]) Type guard
import os
def has_valid_key_file(cert) -> bool:
"""True when cert is a tuple whose key file (2nd element) exists."""
if isinstance(cert, (tuple, list)) and len(cert) == 2:
return os.path.exists(cert[1]) and os.access(cert[1], os.R_OK)
# string form sets key_file=None, so this branch is N/A
return True Try / catch
try:
resp = session.get(url)
except OSError as e:
if "TLS key file" in str(e):
raise RuntimeError(
f"client key missing/unreadable: {session.cert}"
) from e
raise Prevention
- Always deploy the cert and key as a pair; validate both in a startup probe. Give the process user read permission on the key file. Use absolute paths to avoid working-directory ambiguity. If cert+key share one PEM, use the string form to skip the key check.
When it happens
Trigger: Produced when cert=("/path/client.crt", "/path/client.key") is passed to a request or session and the key file (second element) does not exist. Only reachable via the tuple form, since the string form leaves key_file as None.
Common situations: Seen when the cert and key are stored in separate files and only one was deployed, when secret rotation updated the cert but not the key path, when the key file has restrictive permissions that effectively hide it, or when a path typo affects only the key element.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Could not find the TLS certificate file, invalid path
- Could not find a suitable TLS CA certificate bundle…
- Invalid timeout . Pass a (connect, read) timeout tuple, or…
- Please check proxy URL. It is malformed and could be…
- Unsupported event specified, with event name
AI-assisted analysis of psf/requests@8068356288 (2026-08-11).
Data as JSON: /api/errors/b4a8a0373582a990.
Report an issue: GitHub.
Appendix: 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 8068356288)