Skip to content

Rosetta: Node 24+ freezes at 100% CPU; the rosetta-wrapper's --no-opt doesn't cover V8's Maglev #831

Open

Description

@Andy9822

Bug report

Docker Desktop's rosetta-wrapper starts every node under Rosetta with --no-opt -r /proc/.reset, the workaround from docker/for-mac#6998. --no-opt turns off V8's TurboFan, but Node 24 and later also optimize with Maglev, V8's mid-tier compiler, which emits the same x87 code for a floating-point remainder. Under Rosetta on macOS 15.2 that code runs thousands of times slower: a function doing x % 0.25 runs fine until Maglev compiles it, then a few thousand more calls take minutes.

We hit it in a real build. Tailwind CSS 4 runs Number(value) % 0.25 for every class-name candidate, and a Vite production build inside an amd64 image sat on "transforming..." for over 40 minutes. The same build takes 1.5 s natively, and 6 s under Rosetta with node --no-maglev, with byte-identical output.

Expected: Node 24+ under Rosetta runs at about the speed Node 20 and 22 do.
Actual: any optimized float % freezes the process at 100% CPU.

Steps to reproduce

// repro.js
function O(e, t) { const r = Number(e); return r >= 0 && r % t === 0 && String(r) === String(e) }
let c = 0, t = Date.now()
for (let i = 1; i <= 30000; i++) {
  if (O(String(i / 4), 0.25)) c++
  if (i % 5000 === 0) { const n = Date.now(); console.log(`${i} calls: +${n - t} ms`); t = n }
}
docker run --rm --platform linux/amd64 -v "$PWD/repro.js:/repro.js:ro" node:26-slim node /repro.js
docker run --rm --platform linux/amd64 -v "$PWD/repro.js:/repro.js:ro" node:26-slim node --no-maglev /repro.js
Node Result
20.20.2 30,000 calls in about 120 ms
22.23.4 30,000 calls in about 140 ms
24.22.2 5,000 calls in 76 ms, then the next 5,000 not done after 60 s
26.10.0 10,000 calls in 121 ms, then the next 5,000 not done after 3 minutes
26.10.0 with --no-maglev 200,000 calls in 0.14 s

The container process shows the wrapper's flags: /run/rosetta/rosetta /usr/local/bin/node node --no-opt -r /proc/.reset .... Docker Desktop 4.37.2 (engine 27.4.0) on the same Mac behaves the same. On an M5 with macOS 27.0.1 (engine 29.7.2) the same build is fast, so a newer Rosetta likely doesn't have the problem; anyone on macOS 15 or older with Node 24+ does.

Why: V8's x64 Float64Modulus loops on x87 fprem / fnstsw / sahf, in TurboFan (src/compiler/backend/x64/code-generator-x64.cc) and in Maglev, which copies it (src/maglev/x64/maglev-ir-x64.cc). Unoptimized code calls C's fmod instead, which is why the first few thousand calls are fast.

Suggestion: add --no-maglev next to --no-opt in the wrapper's Node workaround. Node refuses --no-maglev in NODE_OPTIONS, so users can't easily add it themselves.

Workaround meanwhile: start the affected node with --no-maglev directly. We start Vite through a two-line script in the image build.

Platform

macOS

Version information

  • Docker Desktop 4.98.0 (Client and Server 29.9.2), "Use Rosetta for x86_64/amd64 emulation" on (default)
  • Also reproduced on Docker Desktop 4.37.2 (engine 27.4.0)
  • macOS 15.2 (24C101), Apple M1 Pro
  • Images: node:20-slim (20.20.2), node:22-slim (22.23.4), node:24-slim (24.22.2), node:26.10.0-slim, --platform linux/amd64

Diagnostics ID

No response

Activity

  1. bsousaa commented on Nov 9, 2026

    @bsousaa
    Collaborator

    please include a diagnostics id in your issue report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions