TypeSafe SDK 0.7.2's default async HTTP stack can abort the interpreter during concurrent TLS connection setup on Linux when truststore 0.10.4 is installed. This is an upstream dependency race, but documenting the workaround here would help SDK users.
The SDK creates an httpx2.AsyncClient, whose default SSL context is truststore.SSLContext in the tested environment. AnyIO offloads its wrap_bio() calls to worker threads. truststore 0.10.4 reconfigures the shared OpenSSL context without the lock used by its socket path, allowing concurrent context mutations.
Upstream tracking: sethmlarson/truststore#221
Fix: sethmlarson/truststore#219 — merged September 29, 2026, but the latest published release checked today is still 0.10.4.
Independent reproduction (October 2)
No TypeSafe API calls, credentials, external connections, pandas, or pyarrow were needed:
- Direct shared-context stress test: 16 threads × 300
wrap_bio() calls, 2 of 5 completed trials aborted with SIGABRT (double free or corruption (fasttop)).
- Actual
anyio.streams.tls.TLSStream.wrap(), using the context from a default AsyncTypeSafeClient and an in-memory failing peer: 16 async workers × 30 attempts, 2/3 trials aborted.
- Same AnyIO probe with
http_client=httpx2.AsyncClient(verify=ssl.create_default_context()): 0/3 native crashes, all 480 attempts per trial reached the expected simulated-peer failure.
- Same AnyIO probe with truststore's
wrap_bio() patched to use the exact lock scope in merged PR #219: 0/3 native crashes.
The async crash stack includes truststore/_openssl.py:38 _configure_context ← truststore/_api.py:144 wrap_bio ← AnyIO's asyncio worker thread. The failing-peer probe exercises context wrapping, not a successful TLS handshake or a live SDK request. Counts demonstrate reproducibility, not an estimated production crash rate.
Minimal network-free reproducer
Run this in a disposable Python process; it may terminate natively.
import faulthandler
import ssl
import threading
import truststore
faulthandler.enable()
ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
def worker():
for _ in range(300):
ctx.wrap_bio(
ssl.MemoryBIO(), ssl.MemoryBIO(),
server_hostname="api.example.com",
)
threads = [threading.Thread(target=worker) for _ in range(16)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("completed")
Workaround
import ssl
import httpx2
from typesafe_sdk import AsyncTypeSafeClient
async with httpx2.AsyncClient(verify=ssl.create_default_context()) as http:
async with AsyncTypeSafeClient(http_client=http) as client:
... # normal SDK calls
This retains certificate verification and hostname checking while bypassing truststore. It uses OpenSSL's configured CA paths instead of truststore's native platform integration. Configured SSL_CERT_FILE/SSL_CERT_DIR or another custom HTTP client can also change the default context, so this does not affect every async configuration.
Suggested action: document the workaround alongside async/concurrent usage (and #10's HTTP/2 example), then recommend the fixed truststore release once published. Reusing a client reduces new handshakes but does not guarantee avoidance.
Test environment: Ubuntu 24.04, Python 3.12.3, OpenSSL 3.0.13, typesafe-sdk 0.7.2, httpx2/httpcore2 2.13.1, AnyIO 4.15.1, truststore 0.10.4.
TypeSafe SDK 0.7.2's default async HTTP stack can abort the interpreter during concurrent TLS connection setup on Linux when truststore 0.10.4 is installed. This is an upstream dependency race, but documenting the workaround here would help SDK users.
The SDK creates an
httpx2.AsyncClient, whose default SSL context istruststore.SSLContextin the tested environment. AnyIO offloads itswrap_bio()calls to worker threads. truststore 0.10.4 reconfigures the shared OpenSSL context without the lock used by its socket path, allowing concurrent context mutations.Upstream tracking: sethmlarson/truststore#221
Fix: sethmlarson/truststore#219 — merged September 29, 2026, but the latest published release checked today is still 0.10.4.
Independent reproduction (October 2)
No TypeSafe API calls, credentials, external connections, pandas, or pyarrow were needed:
wrap_bio()calls, 2 of 5 completed trials aborted with SIGABRT (double free or corruption (fasttop)).anyio.streams.tls.TLSStream.wrap(), using the context from a defaultAsyncTypeSafeClientand an in-memory failing peer: 16 async workers × 30 attempts, 2/3 trials aborted.http_client=httpx2.AsyncClient(verify=ssl.create_default_context()): 0/3 native crashes, all 480 attempts per trial reached the expected simulated-peer failure.wrap_bio()patched to use the exact lock scope in merged PR #219: 0/3 native crashes.The async crash stack includes
truststore/_openssl.py:38 _configure_context←truststore/_api.py:144 wrap_bio← AnyIO's asyncio worker thread. The failing-peer probe exercises context wrapping, not a successful TLS handshake or a live SDK request. Counts demonstrate reproducibility, not an estimated production crash rate.Minimal network-free reproducer
Run this in a disposable Python process; it may terminate natively.
Workaround
This retains certificate verification and hostname checking while bypassing truststore. It uses OpenSSL's configured CA paths instead of truststore's native platform integration. Configured
SSL_CERT_FILE/SSL_CERT_DIRor another custom HTTP client can also change the default context, so this does not affect every async configuration.Suggested action: document the workaround alongside async/concurrent usage (and #10's HTTP/2 example), then recommend the fixed truststore release once published. Reusing a client reduces new handshakes but does not guarantee avoidance.
Test environment: Ubuntu 24.04, Python 3.12.3, OpenSSL 3.0.13, typesafe-sdk 0.7.2, httpx2/httpcore2 2.13.1, AnyIO 4.15.1, truststore 0.10.4.