aio-libs/aiohttp · error · ValueError
fingerprint has invalid length
Error message
fingerprint has invalid length
What it means
Raised as ValueError in Fingerprint.__init__ when the supplied fingerprint bytes are not one of the recognized digest lengths. HASHFUNC_BY_DIGESTLEN maps length 16->md5, 20->sha1, 32->sha256; any other length has no matching hash function and is rejected before TLS pinning can be set up.
Source
Thrown at aiohttp/client_reqrep.py:187
For backwards compatibility, the real_url parameter is optional.
"""
return tuple.__new__(
cls, (url, method, headers, url if real_url is sentinel else real_url)
)
class Fingerprint:
HASHFUNC_BY_DIGESTLEN = {
16: md5,
20: sha1,
32: sha256,
}
def __init__(self, fingerprint: bytes) -> None:
digestlen = len(fingerprint)
hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)
if not hashfunc:
raise ValueError("fingerprint has invalid length")
elif hashfunc is md5 or hashfunc is sha1:
raise ValueError("md5 and sha1 are insecure and not supported. Use sha256.")
self._hashfunc = hashfunc
self._fingerprint = fingerprint
@property
def fingerprint(self) -> bytes:
return self._fingerprint
def check(self, transport: asyncio.Transport) -> None:
if not transport.get_extra_info("sslcontext"):
return
sslobj = transport.get_extra_info("ssl_object")
cert = sslobj.getpeercert(binary_form=True)
got = self._hashfunc(cert).digest()
if got != self._fingerprint:
host, port, *_ = transport.get_extra_info("peername")
raise ServerFingerprintMismatch(self._fingerprint, got, host, port)View on GitHub (pinned to c0ef574e29)
Solutions
- Use a 32-byte sha256 fingerprint (the only currently supported secure option, see error 65).
- Decode hex to bytes: bytes.fromhex(hex_fp) before passing to Fingerprint.
- Generate with: openssl s_client -connect host:443 | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary.
Example fix
# before
fp = aiohttp.Fingerprint(b"2f:ca:9b:...") # wrong length / text
fp = aiohttp.Fingerprint(hex_digest) # str, not bytes
# after
raw = bytes.fromhex("2fca9b...") # exactly 32 bytes
fp = aiohttp.Fingerprint(raw) Defensive patterns
Strategy: validation
Validate before calling
def make_fingerprint(raw: bytes) -> aiohttp.Fingerprint:
if len(raw) != 32:
raise ValueError(f"expected 32-byte sha256 digest, got {len(raw)} bytes")
return aiohttp.Fingerprint(raw) Type guard
def is_sha256_length(raw: bytes) -> bool:
return isinstance(raw, (bytes, bytearray)) and len(raw) == 32 Prevention
- Always generate pins with sha256.
- Decode hex strings with bytes.fromhex before passing.
- Store pins as raw 32-byte bytes, not hex strings, in config.
When it happens
Trigger: Fires at line 186-187 when len(fingerprint) is not in {16,20,32}. Common when passing a hex string instead of raw bytes, a truncated/concatenated fingerprint, or a different hash length (e.g. sha512 = 64 bytes).
Common situations: Passing a hex-encoded fingerprint string (length 64 chars) instead of the 32-byte digest; copying a fingerprint from openssl output without decoding; using SHA-384/512 pinning which aiohttp doesn't support.
Related errors
- md5 and sha1 are insecure and not supported. Use sha256.
- Cannot connect to host {host}:{port} ssl:{ssl} [{ClassName}:
- Cannot connect to host {host}:{port} ssl:{ssl} [{strerror}]
- ssl should be SSLContext, Fingerprint, or bool, got {ssl!r}
- ssl should be SSLContext, Fingerprint, or bool, got {ssl!r}
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/9a39592236d93052.json.
Report an issue: GitHub.