arduino/Arduino · error · ValueError

empty or no certificate

Error message

empty or no certificate

What it means

ssl_match_hostname.match_hostname() raises ValueError('empty or no certificate') when the cert argument is falsy — None or an empty dict. A peer certificate dict (typically from SSLSocket.getpeercert()) is required to perform hostname verification; without it there is nothing to match against.

Source

Thrown at arduino-core/src/processing/app/i18n/python/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py:32

            # When '*' is a fragment by itself, it matches a non-empty dotless
            # fragment.
            pats.append('[^.]+')
        else:
            # Otherwise, '*' matches any dotless fragment.
            frag = re.escape(frag)
            pats.append(frag.replace(r'\*', '[^.]*'))
    return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)

def match_hostname(cert, hostname):
    """Verify that *cert* (in decoded format as returned by
    SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 rules
    are mostly followed, but IP addresses are not accepted for *hostname*.

    CertificateError is raised on failure. On success, the function
    returns nothing.
    """
    if not cert:
        raise ValueError("empty or no certificate")
    dnsnames = []
    san = cert.get('subjectAltName', ())
    for key, value in san:
        if key == 'DNS':
            if _dnsname_to_pat(value).match(hostname):
                return
            dnsnames.append(value)
    if not dnsnames:
        # The subject is only checked when there is no dNSName entry
        # in subjectAltName
        for sub in cert.get('subject', ()):
            for key, value in sub:
                # XXX according to RFC 2818, the most specific Common Name
                # must be used.
                if key == 'commonName':
                    if _dnsname_to_pat(value).match(hostname):
                        return
                    dnsnames.append(value)

View on GitHub (pinned to a0df6e0e83)

Solutions

  1. Check `if not cert:` before calling match_hostname and treat it as a failed/handshake-unverified connection.
  2. Configure the SSL context with CERT_REQUIRED so a peer certificate is always present.
  3. Pass the parsed dict from getpeercert(), not the socket or raw PEM bytes.
  4. Reject connections lacking a certificate rather than proceeding unverified.

Example fix

// before
match_hostname(sock.getpeercert(), hostname)
// after
cert = sock.getpeercert()
if not cert:
    raise SSLError('no peer certificate presented')
match_hostname(cert, hostname)
Defensive patterns

Strategy: validation

Validate before calling

cert = sock.getpeercert()
if not cert:
    raise SSLError('peer presented no certificate')
match_hostname(cert, hostname)

Type guard

def has_peer_certificate(cert):
    return isinstance(cert, dict) and len(cert) > 0

Try / catch

try:
    match_hostname(cert, hostname)
except ValueError as e:
    if 'empty or no certificate' in str(e):
        raise SSLError('no certificate to verify') from e
    raise

Prevention

When it happens

Trigger: Calling match_hostname(sock.getpeercert(), hostname) when the peer sent no certificate (getpeercert() returns None on unauthenticated sessions) or an empty dict; passing the raw socket instead of the cert dict.

Common situations: TLS sessions negotiated without certificate verification or with anonymous cipher suites; forgetting to enable CERT_REQUIRED so the peer cert is never populated; testing harnesses with mock sockets returning None.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of arduino/Arduino@a0df6e0e83 (2026-09-06). Data as JSON: /api/errors/5b9b6c49f0ad7425. Report an issue: GitHub.