macOS: Workers crash with NSCharacterSet fork-safety error after upgrading to 25.1.0 #3526
Preston-Landers
started this conversation in
Issue Triage
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Type
Bug Report
Description
After upgrading from 25.0.3 to 25.1.0, forked workers crash immediately on macOS (Tahoe 26.3, M4 Pro) with the following error:
The crash occurs when the application has established a PostgreSQL connection (via psycopg2-binary) before workers are forked. The same application works fine under 25.0.3 with no other changes.
It does seem like setting --no-control-socket makes the problem go away.
Note that I'm using Python 3.14.3 from python-build-standalone:
cpython-3.14.3+20260203-aarch64-apple-darwin-install_only_stripped.tar.gzSteps to Reproduce (for bugs)
Save as
test_gu.py:Run with:
Configuration
Logs / Error Output
Gunicorn Version
gunicorn 25.1.0
Python Version
Python 3.14.3
Worker Class
sync (default)
Operating System
macOS Tahoe 26.3
Additional Context
Analysis / speculation:
The macOS ObjC runtime aborts forked child processes when it detects that an ObjC class was being initialized on another thread at the time of fork(). The error specifically mentions "in another thread," which suggests that new thread activity in the master process in 25.1.0 is overlapping with fork().
Based on testing,
--no-control-socketappears to resolve the issue, suggesting the new control interface thread is the trigger. The control socket listener thread likely touches system frameworks that initialize ObjC classes, and when fork() occurs while that thread is active, the runtime kills the child.psycopg2-binary directly links libssl and libcrypto, which on macOS trigger ObjC framework initialization (Security.framework / Foundation.framework). Interestingly, psycopg3-binary (
psycopg[binary]) does NOT reproduce the issue - its C extension delegates SSL to libpq rather than linking libssl/libcrypto directly, which appears to change the initialization timing enough to avoid the conflict.Setting
OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YESalso suppresses the crash, but this masks the fork-safety violation rather than resolving it. It's worth noting that while macOS makes this violation fatal and visible, the same underlying condition (forking while threads are active) could potentially cause subtle issues on Linux as well - deadlocks from stale mutexes, etc. - that would be harder to detect.Workarounds:
--no-control-socketOBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES(masks the symptom)Checklist
Beta Was this translation helpful? Give feedback.
All reactions