Issue #22248 has been reported by hornairs (Harry Brundage). ---------------------------------------- Bug #22248: `Errno::NOERROR` from `connect(2)` once a non-main Ractor has more than one Ruby thread https://bugs.ruby-lang.org/issues/22248 * Author: hornairs (Harry Brundage) * Status: Open * ruby -v: 4.0.6 * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- ## Summary On `x86_64-linux`, socket IO in a non-main Ractor fails spuriously as soon as that Ractor contains a second Ruby thread. The connecting thread raises `Errno::NOERROR` — errno 0 surfaced as a `SystemCallError`, whose message is literally `Success`: ``` Errno::NOERROR: Failed to open TCP connection to 127.0.0.1:38975 (Success - connect(2) for "127.0.0.1" port 38975) ``` With a raw `TCPSocket` instead of `Net::HTTP` the VM can abort outright: ``` [BUG] rb_sys_fail_path_in(io_fillbuf, fd:6 ) - errno == 0 ``` What the second thread does doesn't seem to matter. A thread that only calls `sleep`, one that only burns CPU, one doing pipe IO, and one doing socket IO all trigger it. Nothing else I varied matters: a Ractor with a single thread never fails, no matter how much socket work it does or how loaded the machine is, and a second thread in the **main** Ractor never causes it, but a second thread in a non-main Ractor does. This bug made my CI jobs flaky in roughly 10% of builds, where the "second thread" was nothing more exotic than a stub HTTP server standing in for an upstream service alongside the client under test. ## Reproduction See this gist: https://gist.github.com/airhorns/dc676921e02272cc41f8aa15da040e45 ``` ruby ractor_factors.rb interleave 200 ``` Note that the interleaving is important to more reliably trigger the bug. On a shared machine the failure rate swings enormously with host load: run in blocks, the same arrangement scored 196/200 in one build and 0/200 in the next, so blocked counts are not comparable to each other and a 0 says nothing. ## Architecture I have only reproduced this on `x86_64-linux`. On `aarch64-linux` — official `ruby:4.0`, byte-identical `revision 03b6d3f889` — roughly 1500 exchanges of the failing arrangements are clean, including 300 of the arrangement that fails 89/200 on x86, under 1- and 2-CPU quotas, with competing busy loops, and with 8 neighbour threads. `arm64-darwin` is clean too. So expect to need an x86_64 Linux host, ideally a loaded one. ## Environment ``` ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +PRISM [x86_64-linux] Socket.tcp_fast_fallback = true (default; false reproduces identically) RUBY_MAX_CPU unset (=1 does not help) shared CI host, nproc 16, 1-minute load average 3.1-5.9 during the run ``` ## Relation to Bug #21195 [Bug #21195](https://bugs.ruby-lang.org/issues/21195) is the same shape — errno lost around `io_internal_wait`, fixed for 3.3 and 3.4 in 2025 — and the `errno == 0` assertion text is identical. This is a live path in 4.0.6, reached through ordinary socket connect and read, with or without Happy Eyeballs, so I am filing it separately rather than commenting there. I could not find an existing report for the Ractor variant. -- https://bugs.ruby-lang.org/