Issue #21790 has been updated by mame (Yusuke Endoh). Thank you for the report. Since I don't have access to Tahoe, I cannot test this in my own environment. However, I have a few questions to clarify the situation. The change to perform DNS lookups in a dedicated background thread was introduced in Ruby 3.3.0. You mentioned that this affects Ruby 3.2.6 as well. Are you certain it reproduces on 3.2.6? If it fails on 3.2.6, the cause might be unrelated to the background thread, as its behavior should be similar to Python's. Would it be possible to provide a stack trace from the 3.2.6 crash? Though it's just a guess, this might be a bug with `getaddrinfo` on Tahoe itself, but I could be wrong. ---------------------------------------- Bug #21790: `Socket.getaddrinfo` hangs after `fork()` on macOS 26.1 (Tahoe) for IPv4-only hosts https://bugs.ruby-lang.org/issues/21790#change-115797 * Author: adamoffat (Adam Moffat) * Status: Open * ruby -v: 3.3.8 * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- Ruby's `Socket.getaddrinfo` hangs indefinitely in forked child processes on macOS 26.1 (Tahoe) when resolving IPv4-only hostnames. This is a regression that does not occur on macOS 15.x (Sonoma) or earlier. **Ruby version:** ruby 3.3.8 (2025-04-09 revision b200bad6cd) [arm64-darwin24] Also confirmed this affects Ruby 3.2.6 and 3.4.1. **Reproducible script:** ``` ruby require "socket" require "timeout" puts "Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}" Socket.getaddrinfo("api.segment.io", 443, nil, :STREAM) puts "Parent: DNS completed" pid = fork do puts "Child: Attempting DNS resolution..." begin Timeout.timeout(90) do Socket.getaddrinfo("api.segment.io", 443, nil, :STREAM) end puts "Child: SUCCESS" exit 0 rescue Timeout::Error puts "Child: FAILED - hung for 90 seconds" exit 1 end end Process.wait(pid) ``` **Note:** Remove the `Timeout.timeout(90)` wrapper to observe the hang indefinitely. The timeout is included only to allow the script to exit for testing purposes. **Result of reproduce process:** ``` Ruby 3.3.8 on arm64-darwin24 Parent: DNS completed Child: Attempting DNS resolution... Child: FAILED - hung for 90 seconds ``` The child process hangs with one thread consuming 100% CPU. Expected result: The child process should complete DNS resolution successfully, as it does on macOS 15.x and earlier. **Analysis:** Stack trace shows: Main thread: Blocked in `wait_getaddrinfo` → `_pthread_cond_wait` DNS thread: Spinning in `_gai_nat64_second_pass` → `nw_path_access_agent_cache` → `_os_log_preferences_refresh` → `SIGSEGV` The crash occurs in macOS's NAT64 synthesis code path. Ruby's signal handler catches the `SIGSEGV` but cannot recover, causing the DNS thread to spin. **Key observations:** - Only affects IPv4-only hosts. Hosts with IPv6 (like google.com) work correctly. - Using `AF_INET` instead of `AF_UNSPEC` works. `Socket.getaddrinfo("api.segment.io", 443, Socket::AF_INET, :STREAM)` succeeds. - Python is not affected. Python calls `getaddrinfo()` synchronously without a background thread. - Parent must do DNS before fork. If the parent has not called getaddrinfo(), the child works correctly. **Workaround:** - Use `resolv-replace` to bypass the native DNS resolver: `require "resolv-replace"` **Impact:** This breaks all Ruby applications using pre-forking worker models (Resque, Unicorn, Puma, Sidekiq, Passenger) on macOS Tahoe. **Apple Bug Report:** Filed with Apple as Feedback Assistant #FB21364061 ---Files-------------------------------- stack_trace.txt (66.6 KB) ruby_dns_fork_bug.rb (1.02 KB) -- https://bugs.ruby-lang.org/