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

  1. Pass exactly 32 bytes of a SHA-256 digest of the DER-encoded server certificate (preferred).
  2. If you have a hex string, convert it: Fingerprint(bytes.fromhex(hex_str)).
  3. Do not use MD5 (16) or SHA-1 (20) — they raise a separate 'insecure' error; use SHA-256.
  4. 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

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


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)