When macOS 26 (Tahoe) Blocked Python Socket Connections — and How LaunchDaemon Fixed It
TL;DR
On macOS 26 (Tahoe), WebSocket connections from Homebrew-installed Python were failing with errno 65 (No route to host). curl worked fine, but Python kept failing. The culprit: TCC (Transparency, Consent, and Control). The fix: switching from LaunchAgent to LaunchDaemon.
Background
We were building an automated setup script to install OpenClaw (an AI assistant framework) on refurbished Mac minis (M1, macOS 26.0.1 Tahoe) for resale. A Python script running as a LINE Bridge — connecting to a WebSocket relay server — was registered as a LaunchAgent, but the Gateway refused to start.
The logs showed:
socket.connect_ex(('192.168.x.x', 3500)) → errno 65: No route to host
The target was another host on the same LAN. Ping worked. curl worked. But Python failed consistently.
Debugging
Everything We Checked First (All Wrong)
1. IP/Port problem?
ping 192.168.x.x # OK
curl http://192.168.x.x:3500 # OK
nc -zv 192.168.x.x 3500 # OK
All passed. The problem was isolated to Python.
2. Hairpin NAT?
We were initially connecting to an external domain (wss://linebot.techsfree.com). Some routers block connections from inside the LAN to the router's own external IP (no hairpin NAT support). That was a real issue — we switched to the internal IP — but the error persisted.
3. Firewall?
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# Firewall is disabled.
Nope.
The Real Culprit: TCC
Running sudo python3 script.py directly worked. Running via LaunchAgent (user-level) failed. Root succeeded.
macOS TCC (Transparency, Consent, and Control) manages third-party app access to system resources. On macOS 26 Tahoe, Homebrew-installed Python became subject to TCC checks for network connections when launched from user-space (LaunchAgent).
Verification:
# Via LaunchAgent (user-level) → FAIL
launchctl load ~/Library/LaunchAgents/com.openclaw.linebridge.plist
# → errno 65: No route to host
# Direct sudo execution → OK
sudo /opt/homebrew/bin/python3 line_bridge_v2.py
# → Connected to relay server ✓
# subprocess.run(['curl', ...]) → OK
# curl is a system binary and bypasses TCC
The Fix: Promote to LaunchDaemon
LaunchAgents (~/Library/LaunchAgents/) run in user space. LaunchDaemons (/Library/LaunchDaemons/) run at the system level (root) and bypass TCC.
Before (LaunchAgent):
<!-- ~/Library/LaunchAgents/com.openclaw.linebridge.plist -->
<key>Label</key>
<string>com.openclaw.linebridge</string>
After (LaunchDaemon):
<!-- /Library/LaunchDaemons/com.openclaw.linebridge.plist -->
<key>Label</key>
<string>com.openclaw.linebridge</string>
<key>UserName</key>
<string>openclaw</string> <!-- Runs as system daemon but with user file access -->
The UserName key lets you run as a root-level daemon while still reading/writing files as the openclaw user.
# Register
sudo launchctl bootstrap system /Library/LaunchDaemons/com.openclaw.linebridge.plist
sudo launchctl enable system/com.openclaw.linebridge
Result: Connected to relay server ✓ — fixed immediately.
macOS 26 TCC Summary
| Execution method | TCC restricted | Notes |
|---|---|---|
| System binaries (curl, etc.) | No | Always works |
sudo python3 |
No | Root bypasses TCC |
| LaunchAgent + Homebrew Python | Yes | errno 65 |
| LaunchDaemon + UserName key | No | ✅ Recommended |
| subprocess.run(['curl', ...]) | No | Workaround via system binary |
macOS 26 has significantly tightened security. Scripts that worked fine on earlier versions may break — especially resident daemons + third-party runtimes + network connections.
Side Note: Mac Mini Factory Automation
This issue appeared while building a product: refurbished Mac minis pre-installed with OpenClaw, sold as AI assistants.
Unlike GMK Ubuntu units (where Clonezilla image cloning works), Apple Silicon + SIP makes Mac imaging impossible. Instead, we built a set of automation scripts:
-
factory-setup-mac.sh— Full automated install (~15 min) -
per-unit-setup-mac.sh— Per-unit token assignment (5 sec) -
factory-qc-mac.sh— Pre-ship QC (22-item checklist) -
factory-clean-mac.sh— Pre-ship cleanup
The TCC fix is now built into factory-setup-mac.sh — future units will automatically register the LINE Bridge as a LaunchDaemon.
Top comments (0)