Skip to content

Gateway crashes with ERR_INVALID_STATE (FileHandle closed during GC) on Node 26 when handling inbound image media #99263

Description

@synicalkid

Summary

On Node.js 26, the Gateway process crashes with an uncaught ERR_INVALID_STATE exception originating from a FileHandle that is closed by the garbage collector instead of being closed explicitly. The handle points at an inbound image file under the media inbound directory. Node 26 promoted GC-closed FileHandles from a deprecation warning to a hard error, so a latent unclosed-handle leak in the inbound-media path now crashes the process.

Environment

  • OpenClaw: 2026.6.11 (latest stable)
  • Node.js: 26.3.0
  • Platform: linux arm64
  • Gateway mode: local, systemd user service (auto-restarts after crash)

Error

Error [ERR_INVALID_STATE]: A FileHandle object was closed during garbage collection.
This used to be allowed with a deprecation warning but is now considered an error.
Please close FileHandle objects explicitly. File descriptor: <fd>
(<data-dir>/media/inbound/<uuid>.png)
  • code: ERR_INVALID_STATE
  • name: Error
  • No stack trace and no correlated event in the diagnostic ring buffer — consistent with a GC-finalizer throw detached from the original call site. The buffer shows only routine heartbeats immediately before the crash; the handle was opened earlier during inbound-message processing and thrown much later when GC reaped it.

Impact

  • 5 identical uncaught-exception crashes over a ~2-hour window, each triggered around inbound image handling.
  • systemd restarts the Gateway each time, so it self-heals, but every inbound image is a potential crash.
  • Reproducible on any deployment running Node 26, since the behavior change is at the runtime level.

Root cause

Node 26 (per nodejs/node, the change making GC-closed FileHandles throw ERR_INVALID_STATE rather than emit a deprecation warning) surfaces a pre-existing leak: a FileHandle opened in the inbound-media write/read path is never explicitly .close()d (or wrapped so it closes on all paths), so it is finalized by GC.

Expected

Inbound-media file handles are closed explicitly (e.g. try/finally around fs.open, or use fs.readFile/fs.writeFile/stream pipeline which manage the descriptor), so no FileHandle is left for the GC to reap.

Suggested fix

Audit the inbound-media path for fsPromises.open(...) usages whose returned FileHandle is not guaranteed to be closed on every code path, and either close in finally or switch to a higher-level API that owns the descriptor lifecycle.

Notes

  • Confirmed the 2026.7.1-beta.1 changelog and inbound-media code do not address this (node engines floor is still >=22.19.0; no FileHandle/GC fix listed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions