locustio/locust · 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

When `cert` is a (cert, key) tuple, cert_verify assigns conn.key_file and validates it exists, raising OSError if the key file is missing. Both the cert and its private key must be readable for mTLS to work.

Source

Thrown at locust/clients.py:526

                    conn.ca_certs = cert_loc
                else:
                    conn.ca_cert_dir = cert_loc
        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, 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_connection_pool_key_attributes(self, request, verify, cert=None):
        host_params, pool_kwargs = super().build_connection_pool_key_attributes(request, verify, cert)

        if requests_version >= (2, 32, 5) and verify is True:
            pool_kwargs["ssl_context"] = _preloaded_ssl_context

        return host_params, pool_kwargs


# Monkey patch Response class to give some guidance
def _missing_catch_response_True(self, *_args, **_kwargs):
    raise LocustError(
        "If you want to change the state of the request using .success() or .failure(), you must pass catch_response=True. See http://docs.locust.io/en/stable/writing-a-locustfile.html#validating-responses"
    )


Response.success = _missing_catch_response_True  # type: ignore[attr-defined]

View on GitHub (pinned to f391a716e1)

Solutions

  1. Verify the key path exists: `ls -l /path/client.key`
  2. Mount the key file (e.g. as a Kubernetes/Docker secret) and use absolute paths
  3. Check the tuple order: (cert, key), not (key, cert)
  4. Ensure the Locust process has read permission on the key

Example fix

// before
client.get("/", cert=("/certs/client.crt", "/certs/client.key"))  # key not mounted
// after
client.get("/", cert=("/secrets/tls/client.crt", "/secrets/tls/client.key"))  # both mounted
Defensive patterns

Strategy: validation

Validate before calling

import os
key_path = "/path/client.key"
assert os.path.exists(key_path) and os.access(key_path, os.R_OK), f"TLS key missing/unreadable: {key_path}"

Type guard

def has_cert_key_pair(cert):
    return (isinstance(cert, (list, tuple)) and len(cert) == 2
            and all(os.path.exists(p) for p in cert))

Try / catch

try:
    client.get(url, cert=(crt, key))
except OSError as e:
    logger.error("TLS key invalid: %s", e)

Prevention

When it happens

Trigger: Passing `cert=("/path/client.crt", "/path/client.key")` where the key path does not exist on disk.

Common situations: Deploying only the cert but not the key; key stored in a separate secret not mounted; typos or wrong extension (.pem vs .key); permissions blocking read.

Understand the failure class

Related errors


AI-assisted analysis of locustio/locust@f391a716e1 (2026-08-29). Data as JSON: /api/errors/cbe3f73d24c03cd4. Report an issue: GitHub.