What happens
Following setup.md's macOS instructions produces a godot that hangs forever on every headless invocation — --headless --import, --headless --quit, any build gate. The process sits at ~0% CPU and never returns. Nothing on stdout past .NET: Initializing module....
Repro
brew install --cask godot-mono
sudo ln -sf /Applications/Godot_mono.app/Contents/MacOS/Godot /usr/local/bin/godot # setup.md:114
cd <any godot project>
godot --headless --quit # never returns
Cause
Two things compounding:
1. Godot resolves GodotSharp/ relative to the path it was invoked as. Through the symlink it looks in /usr/local/bin/, finds nothing, and raises a fatal error. setup.md:108 already states the rule — "GodotSharp/ must live next to the godot binary. Godot resolves it relative to itself." — and the macOS command two lines below violates it, since the bundle keeps GodotSharp/ in Contents/Resources/ while the symlink points at Contents/MacOS/Godot.
2. On macOS that fatal error is an NSAlert modal, and --headless has no window to dismiss it. sample on the stuck process:
2577 Thread_8627957: Main Thread DispatchQueue_<multiple>
2457 -[NSAlert runModal] (in AppKit) + 196
2433 _NSTryRunModal (in AppKit) + 100
2428 -[NSApplication runModalForWindow:] (in AppKit) + 292
2406 -[NSApplication _doModalLoop:peek:] (in AppKit) + 216
So the failure mode is an indefinite hang rather than an error exit. For an unattended run that is the worst possible shape — the process sits all night at zero CPU with nothing to react to. This applies to any fatal error on macOS, not just this one: a project with no main scene prints Can't run project: no main scene defined and then hangs on the same modal.
Fix
A wrapper script instead of the symlink, plus the trap and its handling in engines/godot.md: wrap every godot call in timeout, treat exit 124 as the failure signal.
Two capture facts confirmed while verifying, also worth recording in the engine guide: on macOS the backend is Metal and there is no xvfb, and --write-movie combined with --headless aborts (Parameter "t" is null, exit 134) — capture has to run windowed.
Verified on
macOS 26.5.2 (Apple M5), Godot 4.7.1.stable.mono, .NET SDK 10.0.302. Full chain green after the fix: dotnet build → build-time SceneTree scene generation (26/26 nodes, pack-validated) → --headless --import → --headless --quit → --write-movie 450 frames → ffmpeg mp4.
Branch with the fix: https://github.com/zjoooooo/godogen/tree/macos-headless-traps
What happens
Following
setup.md's macOS instructions produces agodotthat hangs forever on every headless invocation —--headless --import,--headless --quit, any build gate. The process sits at ~0% CPU and never returns. Nothing on stdout past.NET: Initializing module....Repro
Cause
Two things compounding:
1. Godot resolves
GodotSharp/relative to the path it was invoked as. Through the symlink it looks in/usr/local/bin/, finds nothing, and raises a fatal error.setup.md:108already states the rule — "GodotSharp/ must live next to the godot binary. Godot resolves it relative to itself." — and the macOS command two lines below violates it, since the bundle keepsGodotSharp/inContents/Resources/while the symlink points atContents/MacOS/Godot.2. On macOS that fatal error is an
NSAlertmodal, and--headlesshas no window to dismiss it.sampleon the stuck process:So the failure mode is an indefinite hang rather than an error exit. For an unattended run that is the worst possible shape — the process sits all night at zero CPU with nothing to react to. This applies to any fatal error on macOS, not just this one: a project with no main scene prints
Can't run project: no main scene definedand then hangs on the same modal.Fix
A wrapper script instead of the symlink, plus the trap and its handling in
engines/godot.md: wrap everygodotcall intimeout, treat exit 124 as the failure signal.Two capture facts confirmed while verifying, also worth recording in the engine guide: on macOS the backend is Metal and there is no
xvfb, and--write-moviecombined with--headlessaborts (Parameter "t" is null, exit 134) — capture has to run windowed.Verified on
macOS 26.5.2 (Apple M5), Godot 4.7.1.stable.mono, .NET SDK 10.0.302. Full chain green after the fix:
dotnet build→ build-timeSceneTreescene generation (26/26 nodes, pack-validated) →--headless --import→--headless --quit→--write-movie450 frames → ffmpeg mp4.Branch with the fix: https://github.com/zjoooooo/godogen/tree/macos-headless-traps