Skip to content

CLI crashes with fatal EBADF fd-close-on-GC after successful generation on Node 24 (regression in 2.100.0) #1282

Closed

Description

@DamianReeves

Summary

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.

Activity

  1. DamianReeves commented on Aug 13, 2026

    @DamianReeves
    MemberAuthor

    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).

  2. DamianReeves commented on Aug 13, 2026

    @DamianReeves
    MemberAuthor

    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.

  3. added a commit that references this issue on Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions