RustPython/RustPython · error · ImportError

ObjC runtime library couldn't be loaded

Error message

ObjC runtime library couldn't be loaded

What it means

_SSLProtocolTransport is the transport your protocol sees when TLS is enabled (create_connection(..., ssl=ctx), start_server with SSL). Its write() pushes application data into the SSL state machine and accepts only bytes, bytearray, or memoryview; anything else raises TypeError('data: expecting a bytes-like instance, got ...') before any TLS processing. The encrypted record layer cannot consume str, so no implicit encoding exists on TLS transports either.

Source

Thrown at Lib/_ios_support.py:16

import sys
try:
    from ctypes import cdll, c_void_p, c_char_p, util
except ImportError:
    # ctypes is an optional module. If it's not present, we're limited in what
    # we can tell about the system, but we don't want to prevent the module
    # from working.
    print("ctypes isn't available; iOS system calls will not be available", file=sys.stderr)
    objc = None
else:
    # ctypes is available. Load the ObjC library, and wrap the objc_getClass,
    # sel_registerName methods
    lib = util.find_library("objc")
    if lib is None:
        # Failed to load the objc library
        raise ImportError("ObjC runtime library couldn't be loaded")

    objc = cdll.LoadLibrary(lib)
    objc.objc_getClass.restype = c_void_p
    objc.objc_getClass.argtypes = [c_char_p]
    objc.sel_registerName.restype = c_void_p
    objc.sel_registerName.argtypes = [c_char_p]


def get_platform_ios():
    # Determine if this is a simulator using the multiarch value
    is_simulator = sys.implementation._multiarch.endswith("simulator")

    # We can't use ctypes; abort
    if not objc:
        return None

    # Most of the methods return ObjC objects
    objc.objc_msgSend.restype = c_void_p

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Encode strings: transport.write(data.encode('utf-8')).
  2. Encode once at the protocol boundary and keep the transport layer strictly bytes.
  3. Convert other objects explicitly: bytes(obj) or memoryview(obj).
  4. Annotate the write path with bytes-only type hints and enforce with mypy/pyright.

Example fix

# before
reader, writer = await asyncio.open_connection(host, port, ssl=ctx)
writer.write('GET / HTTP/1.0\r\n\r\n')

# after
reader, writer = await asyncio.open_connection(host, port, ssl=ctx)
writer.write(b'GET / HTTP/1.0\r\n\r\n')
# or for dynamic strings:
writer.write(request.encode('utf-8'))
Defensive patterns

Strategy: type-guard

Validate before calling

def to_wire(data):
    if isinstance(data, str):
        return data.encode('utf-8')
    if not isinstance(data, (bytes, bytearray, memoryview)):
        raise TypeError(f'not writable by TLS transport: {type(data).__name__}')
    return data

writer.transport.write(to_wire(data))  # or writer.write(to_wire(data))

Type guard

def is_tls_wire_bytes(data: object) -> bool:
    """Types accepted by _SSLProtocolTransport.write()."""
    return isinstance(data, (bytes, bytearray, memoryview))

Prevention

When it happens

Trigger: transport.write('hello') on a TLS connection; passing int/None/dict; feeding json.dumps() output (a str) to the transport returned by open_connection(..., ssl=ctx); code that 'worked' with plain sockets in a library tolerating str.

Common situations: Adding TLS to a plain-socket protocol and discovering payload types; producers handing unicode strings after a refactor removed encoding; wrapping third-party transports that accept str in their own send helpers.

Related errors


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