RustPython/RustPython · error · OSError

Multiple exceptions: {}

Error message

Multiple exceptions: {}

What it means

OSError raised by create_connection when all_errors is False (default), more than one address was tried, and the collected exceptions have differing str() representations. asyncio collapses identical failures into one but combines distinct messages into a single OSError prefixed 'Multiple exceptions: '.

Source

Thrown at Lib/asyncio/base_events.py:1176

                    happy_eyeballs_delay,
                    loop=self,
                ))[0]  # can't use sock, _, _ as it keeks a reference to exceptions

            if sock is None:
                exceptions = [exc for sub in exceptions for exc in sub]
                try:
                    if all_errors:
                        raise ExceptionGroup("create_connection failed", exceptions)
                    if len(exceptions) == 1:
                        raise exceptions[0]
                    elif exceptions:
                        # If they all have the same str(), raise one.
                        model = str(exceptions[0])
                        if all(str(exc) == model for exc in exceptions):
                            raise exceptions[0]
                        # Raise a combined exception so the user can see all
                        # the various error messages.
                        raise OSError('Multiple exceptions: {}'.format(
                            ', '.join(str(exc) for exc in exceptions)))
                    else:
                        # No exceptions were collected, raise a timeout error
                        raise TimeoutError('create_connection failed')
                finally:
                    exceptions = None

        else:
            if sock is None:
                raise ValueError(
                    'host and port was not specified and no sock specified')
            if sock.type != socket.SOCK_STREAM:
                # We allow AF_INET, AF_INET6, AF_UNIX as long as they
                # are SOCK_STREAM.
                # We support passing AF_UNIX sockets even though we have
                # a dedicated API for that: create_unix_connection.
                # Disallowing AF_UNIX in this method, breaks backwards
                # compatibility.

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Switch on exc.errno / exc.__context__ rather than parsing the combined message
  2. Pass all_errors=True and handle the ExceptionGroup to get each failure separately
  3. Constrain family= to the stack that works in your environment to reduce mixed failures

Example fix

# before
except OSError as e:
    if 'refused' in str(e): ...

# after
except OSError as e:
    if e.errno == errno.ECONNREFUSED: ...
# or per-attempt detail:
#   await loop.create_connection(p, h, port, all_errors=True) + except*
Defensive patterns

Strategy: try-catch

Try / catch

try:
    tr, pr = await loop.create_connection(p, h, port)
except OSError as e:
    cause = e.__context__  # often holds the first underlying error
    log.warning('connect failed errno=%s cause=%r', e.errno, cause)
    raise

Prevention

When it happens

Trigger: loop.create_connection(proto, host, port) against a dual-stack host where the IPv6 attempt gives 'Network is unreachable' and the IPv4 attempt gives 'Connection refused'; multiple A records with different per-address failures.

Common situations: Dual-stack clients on partially IPv6-capable networks; CDN hosts with several IPs where some are filtered; retry/monitoring logic that parses exception text instead of errno.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/4d75c6a99d35fce8. Report an issue: GitHub.