aio-libs/aiohttp · error · ValueError
fingerprint has invalid length
Error message
fingerprint has invalid length
What it means
Raised by Fingerprint.__init__ when the supplied fingerprint bytes are not exactly 16, 20, or 32 bytes long. aiohttp maps fingerprint length to a hash function (16=md5, 20=sha1, 32=sha256) and any other length has no mapping, so it raises ValueError. The fingerprint is used to pin/verify the TLS server certificate.
Solutions
- Pass exactly 32 bytes of a SHA-256 digest of the DER-encoded server certificate (preferred).
- If you have a hex string, convert it: Fingerprint(bytes.fromhex(hex_str)).
- Do not use MD5 (16) or SHA-1 (20) — they raise a separate 'insecure' error; use SHA-256.
- SHA-512 fingerprints are not supported by Fingerprint; switch to SHA-256 or implement a custom ssl.SSLContext.
Example fix
# before
fp = Fingerprint(b'\x11' * 64) # 64 bytes -> ValueError
fp = Fingerprint('AB:CD:EF...') # str, not bytes -> ValueError
# after — 32-byte SHA-256 of the DER cert
fp = Fingerprint(bytes.fromhex('a1b2c3...')) # exactly 64 hex chars = 32 bytes
ssl_ctx = ssl.create_default_context()
await session.get(url, ssl=fp) Defensive patterns
Strategy: type-guard
Validate before calling
def make_fingerprint(raw: bytes):
if len(raw) not in (16, 20, 32):
raise ValueError(f'fingerprint must be 16/20/32 bytes, got {len(raw)}')
from aiohttp import Fingerprint
return Fingerprint(raw) Type guard
def is_valid_fingerprint_length(fp: bytes) -> bool:
return isinstance(fp, (bytes, bytearray)) and len(fp) in (16, 20, 32) Try / catch
from aiohttp import Fingerprint
try:
fp = Fingerprint(raw_bytes)
except ValueError as e:
if 'invalid length' in str(e):
raise ValueError('Pass a 32-byte SHA-256 digest of the DER cert')
raise Prevention
- Always derive fingerprints with SHA-256 over the DER cert (32 bytes).
- Convert hex to bytes explicitly with bytes.fromhex before constructing.
- Never pass hex strings, PEM text, or 64-byte digests to Fingerprint.
When it happens
Trigger: Calling Fingerprint(some_bytes) where len(some_bytes) is not 16, 20, or 32 — e.g. passing a 64-byte SHA-512 digest, a base64/hex string (not raw bytes), or a truncated value. Construction fails immediately.
Common situations: Copying a hex-encoded fingerprint string instead of raw bytes; using SHA-512 (64 bytes) which aiohttp's Fingerprint does not support; passing a DER cert or a public-key pin (SPKI) which is a different length; truncating or padding a digest.
Related errors
- md5 and sha1 are insecure and not supported. Use sha256.
- Bad HTTP method in status line
- Method cannot contain non-token characters
- ssl should be SSLContext, Fingerprint, or bool, got
- 1002
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/9a39592236d93052.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)