You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CLI crashes with fatal EBADF fd-close-on-GC after successful generation on Node 24 (regression in 2.100.0) #1282
Since morphir-elm 2.100.0, running morphir make under Node 24 can complete generation successfully and then die with a fatal uncaught exception while the process winds down:
Compiled in DEV mode. Follow the advice at https://elm-lang.org/0.19.1/optimize ...
<anonymous_script>:0
[Error: EBADF: Closing file descriptor 22 on garbage collection failed, close] {
errno: -9,
code: 'EBADF',
syscall: 'close'
}
Node.js v24.19.0
The process exits 1, so any build system driving the CLI reports failure even though the IR was produced. 2.99.0 does not exhibit this under the same Node version.
Environment
morphir-elm: 2.100.0 (regression; 2.99.0 is fine)
Node.js: v24.19.0 (linux x64, GitHub Actions ubuntu-latest; also reproduced across reruns)
Invocation: morphir make via the finos/morphir-scala Mill build against small Elm projects (the unit-test-framework example projects in morphir-scala)
Evidence that it is per-process, not load-related
We first suspected contention between parallel CLI invocations, but the crash reproduces with fully serialized execution (-j 1, one Node process at a time): two independent invocations each crashed the same way in the same run (failing job with serial execution). Across two PRs we saw five consecutive occurrences; earlier occurrences on the same infrastructure were intermittent (example).
Analysis
EBADF on close inside a garbage-collection finalizer means a file descriptor was closed twice: something closes an fd directly while a handle object still owns it, and when that handle is collected its finalizer close fails. Recent Node makes that failure a fatal uncaught exception rather than a warning, which is why the crash lands after all useful work is done. The regression window is whatever changed fd handling between 2.99.0 and 2.100.0 in the CLI path exercised by morphir make.
Impact and downstream workaround
finos/morphir-scala CI was fully blocked and has pinned back to 2.99.0 with a renovate exclusion for exactly 2.100.0 (finos/morphir-scala#963), so a fixed later release will be picked up automatically. Happy to test a candidate fix from a branch or prerelease.
Correction to the version window in the report above: after pinning back to 2.99.0, the same fatal EBADF crash reproduced under 2.99.0 as well (job), while 2.100.0 has also passed on reruns. So this is not a 2.99.0-to-2.100.0 regression; it is a probabilistic per-process crash affecting at least both of those versions under Node.js v24.19.0, whose occurrence rate on GitHub-hosted ubuntu runners increased sharply around 2026-08-13 (possibly a runner-image rollout). The double-close analysis stands; only the regression-window claim is withdrawn. Downstream we are mitigating with build-level retries (finos/morphir-scala#964).
Further characterization: the crash trigger is run-scoped. In one CI job the same project crashed identically on three consecutive retry attempts (six EBADF occurrences), while other runs of identical code on the same commit pass everything — consistent with runner-host variance selecting whether the GC-finalizer close failure fires, and explaining the historical rerun-passes pattern. Within-process retries do not help. Downstream we now neutralize the mechanism with a NODE_OPTIONS preload that swallows exactly the EBADF close uncaught exception after work completes (finos/morphir-scala#965); that shim doubles as a demonstration of a possible interim guard inside the CLI itself.
Activity
DamianReeves commentedon Aug 13, 2026
Correction to the version window in the report above: after pinning back to 2.99.0, the same fatal EBADF crash reproduced under 2.99.0 as well (job), while 2.100.0 has also passed on reruns. So this is not a 2.99.0-to-2.100.0 regression; it is a probabilistic per-process crash affecting at least both of those versions under Node.js v24.19.0, whose occurrence rate on GitHub-hosted ubuntu runners increased sharply around 2026-08-13 (possibly a runner-image rollout). The double-close analysis stands; only the regression-window claim is withdrawn. Downstream we are mitigating with build-level retries (finos/morphir-scala#964).
fix(ci): retry crashed morphirIR tasks up to three times (#964)
DamianReeves commentedon Aug 13, 2026
Further characterization: the crash trigger is run-scoped. In one CI job the same project crashed identically on three consecutive retry attempts (six EBADF occurrences), while other runs of identical code on the same commit pass everything — consistent with runner-host variance selecting whether the GC-finalizer close failure fires, and explaining the historical rerun-passes pattern. Within-process retries do not help. Downstream we now neutralize the mechanism with a NODE_OPTIONS preload that swallows exactly the EBADF close uncaught exception after work completes (finos/morphir-scala#965); that shim doubles as a demonstration of a possible interim guard inside the CLI itself.
fix(elm-tooling): retain NODE_OPTIONS in the Elm process environment
fix(ci): neutralize the EBADF fd-close-on-GC crash with a node preloa…
fix(cli2): close file streams and avoid get-uri fd leak (#1282)
feat(buildkit): buildkit core with the typed outcome executor, morphi…
Merge pull request #1283 from finos/fix/ebadf-guard-1282
fix(cli): guard Node 24 EBADF fd-close-on-GC crash (finos#1282)