fix(deps): bundle socksio so httpx Client doesn't crash inside fence sandbox#96
Merged
sohil-kshirsagar merged 2 commits intomainfrom May 8, 2026
Merged
Conversation
httpx evaluates proxy config eagerly inside Client.__init__ — when fence injects ALL_PROXY=socks5h://localhost:1080 and the customer's app constructs an httpx.Client at module load time, httpx tries to import socksio to build a SOCKS5 transport. Without socksio installed, the import raises and the WSGI/Flask app fails to start before serving a single request. Bundling socksio as a hard dep (~100KB, pure Python, MIT, zero transitive deps) covers every Python customer using httpx without any code or config change on their end. The SOCKS transport gets constructed but never serves a request — the SDK's monkey-patches on Client.send sit above the transport layer, so during replay the proxy is dormant.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5ead518. Configure here.
jy-tan
approved these changes
May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Adds
socksioas a hard dep oftusk-drift-python-sdkso that any Python service constructinghttpx.Client(...)at module load time doesn't crash during Tusk Drift replay withImportError: Using SOCKS proxy, but the 'socksio' package is not installed.Why
The replay sandbox is built on
fence, which runs local SOCKS5 + HTTP proxies and injects these into the sandboxed service's environment so well-behaved HTTP libraries route through it:httpxevaluates proxy config eagerly insideClient.__init__— the moment it seesALL_PROXY=socks5h://..., it tries to importsocksioto construct a SOCKS5 transport. Ifsocksioisn't installed, it raisesImportErrorat construction. So a perfectly normal Python pattern like:…blows up at import time during replay (but not in prod, staging, or local dev, because none of those have
ALL_PROXY=socks5h://...set). The traceback ends with:The WSGI/Flask/Django app never finishes loading, the drift CLI's readiness check fails, and the run errors out with
service failed to become ready within 3m0s. From the customer's POV this looks like a bug in their code or our SDK — but the actual cause is an environmental side-effect of fence's proxy mechanism.Why bundling here
Three options were considered:
httpx.Client(..., trust_env=False)per Clientsocksioto customer depssocksioin the SDKBundling is the lowest-friction long-term fix. socksio is ~100KB, pure Python, MIT-licensed, with zero transitive deps — essentially free to ship. It eliminates the failure mode without any customer involvement.
What socksio does at runtime
Effectively nothing visible:
httpx.Client(...)readsALL_PROXY=socks5h://..., imports socksio successfully, builds a SOCKS5 transport, attaches it to the Client's_mounts. The Client constructor returns normally; Flask/WSGI starts. When app code callsclient.get(...), the SDK's monkey-patch onhttpx.Client.sendintercepts above the transport layer and returns a recorded mock — the SOCKS transport never actually serves a request.The only behavior change is "Client constructor doesn't crash when the env happens to have a SOCKS proxy URL set."
Why only
socksioand not alsoPySocks/aiohttp-socksConsidered bundling broader SOCKS support for
requests/urllib3/aiohttp, but:httpxis unique in that it eagerly evaluates proxy config insideClient.__init__. The SOCKS-related import fires at construction, before any monkey-patch can intercept.requests,urllib3, andaiohttpall evaluate proxy config lazily on.send()/.request(). The SDK's monkey-patches onSession.send/PoolManager.urlopen/ClientSession.sendsit above proxy resolution, so a missingPySocks/aiohttp-sockswould only matter for un-intercepted traffic — which generally indicates a bigger problem (replay isn't being mocked) and isn't worth a default dep.Will document a per-case workaround (
pip install requests[socks], etc.) if a request-time SOCKS failure ever shows up.Risk / blast radius
ALL_PROXY/HTTPS_PROXY/HTTP_PROXYis set with a SOCKS scheme AND the process constructs an httpx Client. In every other case socksio is unused.