python/cpython · error · TypeError

2-tuple is expected

Error message

2-tuple is expected

What it means

Raised by create_datagram_endpoint() when local_addr or remote_addr is not a 2-tuple. For INET families the address form is (host, port); passing a bare string, a 3/4-tuple, a list, or None-ish values triggers this TypeError before any name resolution.

Source

Thrown at Lib/asyncio/base_events.py:1442

                        if stat.S_ISSOCK(os.stat(local_addr).st_mode):
                            os.remove(local_addr)
                    except FileNotFoundError:
                        pass
                    except OSError as err:
                        # Directory may have permissions only to create socket.
                        logger.error('Unable to check or remove stale UNIX '
                                     'socket %r: %r',
                                     local_addr, err)

                addr_pairs_info = (((family, proto),
                                    (local_addr, remote_addr)), )
            else:
                # join address by (family, protocol)
                addr_infos = {}  # Using order preserving dict
                for idx, addr in ((0, local_addr), (1, remote_addr)):
                    if addr is not None:
                        if not (isinstance(addr, tuple) and len(addr) == 2):
                            raise TypeError('2-tuple is expected')

                        infos = await self._ensure_resolved(
                            addr, family=family, type=socket.SOCK_DGRAM,
                            proto=proto, flags=flags, loop=self)
                        if not infos:
                            raise OSError('getaddrinfo() returned empty list')

                        for fam, _, pro, _, address in infos:
                            key = (fam, pro)
                            if key not in addr_infos:
                                addr_infos[key] = [None, None]
                            addr_infos[key][idx] = address

                # each addr has to have info for each (family, proto) pair
                addr_pairs_info = [
                    (key, addr_pair) for key, addr_pair in addr_infos.items()
                    if not ((local_addr and addr_pair[0] is None) or
                            (remote_addr and addr_pair[1] is None))]

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass exactly a 2-tuple of (host, port), e.g. ('127.0.0.1', 9999)
  2. If the value came from getaddrinfo(), take entry[4][0:2] or feed the original host/port instead
  3. Unpack config values: remote_addr=tuple(addr_pair[:2])

Example fix

# before
await loop.create_datagram_endpoint(factory, remote_addr=('127.0.0.1', 53, 0, 0))

# after
await loop.create_datagram_endpoint(factory, remote_addr=('127.0.0.1', 53))
Defensive patterns

Strategy: validation

Validate before calling

def as_addr_pair(a) -> tuple:
    if not (isinstance(a, tuple) and len(a) == 2):
        raise TypeError(f'expected (host, port) 2-tuple, got {a!r}')
    return a
local_addr = as_addr_pair(local_addr) if local_addr else None
remote_addr = as_addr_pair(remote_addr) if remote_addr else None

Type guard

def is_host_port(a: object) -> TypeGuard[tuple[str, int]]:
    return isinstance(a, tuple) and len(a) == 2 and isinstance(a[0], str) and isinstance(a[1], int)

Prevention

When it happens

Trigger: loop.create_datagram_endpoint(factory, remote_addr='127.0.0.1') or remote_addr=('127.0.0.1', 9000, 0, 0) or remote_addr=['127.0.0.1', 9000].

Common situations: Passing a pre-resolved getaddrinfo() 5-tuple instead of (host, port); forgetting to unpack a config pair; using a list literal instead of a tuple.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/52b7df978c049ecc. Report an issue: GitHub.