Skip to content

fix: ruflo memory subcommands hang after completion — process.exit() missing from CLI entry point #1453

@marioja

Description

@marioja

Summary

All ruflo memory subcommands (init, stats, store, search, list, cleanup, compress) hang indefinitely after completing their work. The process never exits because ONNX/WASM worker threads keep the Node.js event loop alive.

Related: PR #1441 implements the fix.

Root Cause

When any memory subcommand runs, ensureInitialized() in memory-tools.ts loads the ONNX embedding model (all-MiniLM-L6-v2 via @xenova/transformers). The ONNX runtime spawns worker threads that are never terminated, preventing Node.js from exiting naturally after the command completes.

The CLI entry point (bin/cli.js) called cli.run() but never called process.exit(0) on success:

// Before (hangs):
cli.run().catch((error) => {
  console.error('Fatal error:', error.message);
  process.exit(1);
});

Only the error path had process.exit(). The success path relied on Node.js exiting naturally — which never happens because of the ONNX worker threads.

Why the fix belongs in bin/cli.js, not in individual commands

The original inline fix (issue #1428) placed setTimeout(() => process.exit(0), 500).unref() inside initMemoryCommand. This was wrong because:

  1. Only fixed one command. Every memory subcommand triggers the same ONNX load via ensureInitialized(), so every one hangs. Fixing them individually requires 20+ call sites.

  2. Commands shouldn't call process.exit(). A command's action function is a library method — it returns a result object ({ success, data }) to its caller. Calling process.exit() inside it breaks testability (process.exit mocks throw in tests) and violates separation of concerns.

  3. The entry point is the right place. bin/cli.js is the process boundary. Adding .then(() => process.exit(0)) there means all commands benefit — memory, neural, or any future command that loads ONNX.

Fix (PR #1441)

// After (exits cleanly):
cli.run().then(() => {
  process.exit(0);
}).catch((error) => {
  console.error('Fatal error:', error.message);
  process.exit(1);
});

Also removed:

  • The inline setTimeout/process.exit hack from initMemoryCommand
  • All scheduleExit() call sites from memory.ts

Testing

# Before fix: hangs until killed
timeout 10 node bin/cli.js memory stats   # exit 124 (timeout)

# After fix: exits immediately
timeout 10 node bin/cli.js memory stats   # exit 0

Unit tests: 32 passed, 1 skipped (unchanged from baseline).

Environment

  • Windows 11 / WSL2
  • Node.js v20+
  • Any platform where ONNX runtime loads successfully

Related

🤖 Generated with claude-flow

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions