ssl.SSLError: [SSL] PEM lib in Python (tested)
ssl.SSLError: [SSL] PEM lib (_ssl.c:...) from load_cert_chain() is OpenSSL saying it couldn't load your certificate or key, without saying why. We generated test certificates and broke them in every common way to see which messages you get and how to tell the causes apart.
1. Every cause we found gives the same "PEM lib"
import os, ssl
os.chdir("/tmp/pe") # cert.pem/key.pem made with: openssl req -x509 -newkey rsa:2048 -nodes ...
open("empty.pem", "w").close()
cases = [
("cert + matching key", ("cert.pem", "key.pem")),
("cert+key in one file", ("combined.pem", None)),
("cert only, key missing", ("cert.pem", None)),
("key and cert swapped", ("key.pem", "cert.pem")),
("key from another cert", ("cert.pem", "other_key.pem")),
("DER (binary) certificate", ("cert.der", "key.pem")),
("empty file", ("empty.pem", "key.pem")),
("path does not exist", ("nope.pem", "key.pem")),
]
for label, (certfile, keyfile) in cases:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
try:
ctx.load_cert_chain(certfile, keyfile)
print(f"{label:26} OK")
except Exception as e:
print(f"{label:26} {type(e).__module__}.{type(e).__name__}: {e}")
Output:
cert + matching key OK
cert+key in one file OK
cert only, key missing ssl.SSLError: [SSL] PEM lib (_ssl.c:4114)
key and cert swapped ssl.SSLError: [SSL] PEM lib (_ssl.c:4093)
key from another cert ssl.SSLError: [X509: KEY_VALUES_MISMATCH] key values mismatch (_ssl.c:4114)
DER (binary) certificate ssl.SSLError: [SSL] PEM lib (_ssl.c:4093)
empty file ssl.SSLError: [SSL] PEM lib (_ssl.c:4093)
path does not exist builtins.FileNotFoundError: [Errno 2] No such file or directory
Measured on Python 3.13.5 (OpenSSL 3, Debian): ssl.SSLError: [SSL] PEM lib came from four different problems: a certificate file with no key and no keyfile, the cert and key arguments swapped, a DER (binary) certificate, and an empty file. Only a key that belongs to a different certificate got its own message, KEY_VALUES_MISMATCH. A missing path is a plain FileNotFoundError that doesn't name the file.
A small clue in our build: the number in (_ssl.c:NNNN) was 4093 when the certificate failed to load and 4114 when the key did. The numbers change between Python builds, so compare them against a case you know, not against these values.
2. Encrypted private keys: the hidden prompt
import os, ssl
os.chdir("/tmp/pe") # enc_key.pem = key.pem encrypted with: openssl pkey -aes256 -passout pass:secret
for label, kw in [("no password given", {}),
("wrong password", {"password": "guess"}),
("right password", {"password": "secret"}),
("password callback", {"password": lambda: os.environ.get("KEY_PASS", "secret")})]:
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
try:
ctx.load_cert_chain("cert.pem", "enc_key.pem", **kw)
print(f"{label:18} OK")
except Exception as e:
print(f"{label:18} {type(e).__module__}.{type(e).__name__}: {e}")
# CA bundles go to load_verify_locations, and it says something different
ctx = ssl.create_default_context()
for f in ("key.pem", "empty.pem"):
try:
ctx.load_verify_locations(cafile=f)
print(f"load_verify_locations({f}) OK")
except Exception as e:
print(f"load_verify_locations({f}) {type(e).__name__}: {e}")
Output:
no password given builtins.OSError: [Errno 22] Invalid argument
wrong password ssl.SSLError: [SSL] PEM lib (_ssl.c:4114)
right password OK
password callback OK
load_verify_locations(key.pem) SSLError: [X509: NO_CERTIFICATE_OR_CRL_FOUND] no certificate or crl found (_ssl.c:4346)
load_verify_locations(empty.pem) SSLError: [X509: NO_CERTIFICATE_OR_CRL_FOUND] no certificate or crl found (_ssl.c:4346)
Measured: with an encrypted key and no password=, OpenSSL printed Enter PEM pass phrase: to the terminal and tried to read it. With no stdin (a service, cron, a container) we got OSError: [Errno 22] Invalid argument; in an interactive shell it would sit waiting for input. A wrong password gives the same PEM lib as the other causes. Pass password= as a string or a callable, e.g. one that reads an environment variable. CA bundles belong in load_verify_locations(), which reports NO_CERTIFICATE_OR_CRL_FOUND for a key file or an empty file instead of PEM lib.
3. Look inside the files before you blame OpenSSL
import os, re
os.chdir("/tmp/pe")
def pem_report(path):
data = open(path, "rb").read()
if not data.strip():
return "EMPTY"
blocks = re.findall(rb"-----BEGIN ([A-Z0-9 ]+)-----", data)
if not blocks:
return f"NOT PEM (first bytes {data[:4].hex()}: DER? convert with openssl x509 -inform DER)"
return ", ".join(b.decode() for b in blocks)
for f in ("cert.pem", "key.pem", "enc_key.pem", "combined.pem", "cert.der", "empty.pem"):
print(f"{f:13} {pem_report(f)}")
Output:
cert.pem CERTIFICATE
key.pem PRIVATE KEY
enc_key.pem ENCRYPTED PRIVATE KEY
combined.pem CERTIFICATE, PRIVATE KEY
cert.der NOT PEM (first bytes 30820309: DER? convert with openssl x509 -inform DER)
empty.pem EMPTY
Measured: reading the -----BEGIN ...----- lines tells the cases apart. certfile must contain CERTIFICATE (it may also contain the key, as in combined.pem). keyfile must contain PRIVATE KEY (with ENCRYPTED meaning you need password=). A file starting with 30 82 is DER: convert it with openssl x509 -inform DER -in cert.der -out cert.pem. For a mismatch, compare the public keys: openssl x509 -noout -pubkey -in cert.pem and openssl pkey -pubout -in key.pem must print the same thing. For certificate verification errors on the client side, see our CERTIFICATE_VERIFY_FAILED guide.
Frequently asked questions
What does ssl.SSLError: [SSL] PEM lib mean in Python?
OpenSSL couldn't load the certificate or key from the file. We measured four causes giving the same message: no key provided, cert and key swapped, a DER certificate, and an empty file. A wrong key password gives it too.
What does KEY_VALUES_MISMATCH mean?
The private key doesn't belong to the certificate. We measured it by loading a cert with a key from another certificate.
Why does load_cert_chain hang or ask for a PEM pass phrase?
The key file is encrypted and no password= was given, so OpenSSL prompts on the terminal. We measured the prompt, and OSError Errno 22 when there was no stdin. Pass password= as a string or callable.
How do I convert a DER certificate to PEM?
openssl x509 -inform DER -in cert.der -out cert.pem. We measured a DER certificate giving PEM lib in load_cert_chain.
Can the certificate and key be in one file?
Yes. We measured load_cert_chain('combined.pem') with the certificate followed by the key loading fine without keyfile.
Written with AI assistance. Measured on 2026-10-09 on Debian with Python 3.13.5 and OpenSSL 3 using self-signed test certificates made with openssl: eight load_cert_chain inputs, encrypted keys with no, wrong and correct passwords, load_verify_locations, and a PEM inspection helper. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; Python's ssl module and OpenSSL are free.