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_pView on GitHub (pinned to aaeab4f754)
Solutions
- Encode strings: transport.write(data.encode('utf-8')).
- Encode once at the protocol boundary and keep the transport layer strictly bytes.
- Convert other objects explicitly: bytes(obj) or memoryview(obj).
- 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
- Keep the TLS write path strictly bytes - encode strings at the producer
- Apply the same to_wire() choke point used for plain sockets
- Use writelines() with bytes elements for batched writes
- Add bytes-only type hints and enforce with mypy/pyright
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
- expected str, bytes or os.PathLike object, not {path_type.__
- Socket cannot be of type SSLSocket
- transport should be _FlowControlMixin instance
- sslcontext is expected to be an instance of ssl.SSLContext,
- data argument must be a bytes-like object, not {type(data)._
AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17).
Data as JSON: /api/errors/e7afa253a30cfe3b.
Report an issue: GitHub.