Skip to content

fix(tui): pass --expose-gc as a node CLI flag, not via NODE_OPTIONS (#17187) - #17200

Closed
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/tui-expose-gc-node-options-17187
Closed

fix(tui): pass --expose-gc as a node CLI flag, not via NODE_OPTIONS (#17187)#17200
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/tui-expose-gc-node-options-17187

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Summary

  • hermes --tui immediately exits with --expose-gc is not allowed in NODE_OPTIONS on Node ≥ 20 because the launcher set NODE_OPTIONS="--max-old-space-size=8192 --expose-gc" (added in fix(tui): harden against Node V8 OOM + GatewayClient leaks + resize perf #13231).
  • Move --expose-gc from the env var onto the node argv inside _make_tui_argv for the two node-direct launch paths. Keep --max-old-space-size=8192 in NODE_OPTIONS, where it's allowed and where user overrides still merge correctly.
  • Adds 4 regression tests (env-var construction + both node-direct argv branches).

The bug

hermes_cli/main.py:1126-1136 (pre-fix) appended --expose-gc to whatever the user had in NODE_OPTIONS:

_tokens = env.get("NODE_OPTIONS", "").split()
if not any(t.startswith("--max-old-space-size=") for t in _tokens):
    _tokens.append("--max-old-space-size=8192")
if "--expose-gc" not in _tokens:
    _tokens.append("--expose-gc")
env["NODE_OPTIONS"] = " ".join(_tokens)

--expose-gc is a V8 flag. Node has an explicit allow-list for NODE_OPTIONS and refuses anything outside it on startup, so the spawned process aborts before the TUI loads:

$ hermes --tui
/home/cimon/.local/bin/node: --expose-gc is not allowed in NODE_OPTIONS

The reporter (#17187) hit this on Ubuntu 24.04 with Node 20.12.2, with their own NODE_OPTIONS empty — the launcher itself was the source. unset NODE_OPTIONS doesn't help because the launcher re-injects the flag every run.

--max-old-space-size=… is on Node's allow-list, which is why the cap added in #13231 still works; only the --expose-gc line trips the rejection.

The fix

_make_tui_argv already returns argv tuples like [node, dist/entry.js] for the two production paths (HERMES_TUI_DIR fast path + built-bundle fallback). V8 flags pass through fine when given on the node CLI, so the patch just inserts --expose-gc between the binary and the script:

return [node, "--expose-gc", str(p / "dist" / "entry.js")], p

…and drops the --expose-gc block from the NODE_OPTIONS construction in _launch_tui. --max-old-space-size=8192 continues to be set via env so the existing user-merge semantics (respect any user-supplied higher cap) are preserved.

The dev paths (tsx src/entry.tsx, npm start) aren't node-direct, so they don't pick up --expose-gc. Those paths are maintainer-only and were already broken on Node 20+ under the old code, so this is a pragmatic trade-off rather than a regression.

Test plan

  • Focused regression test: tests/hermes_cli/test_tui_node_options_expose_gc.py — 4 tests covering the env-var construction in _launch_tui (no --expose-gc injected, user NODE_OPTIONS preserved without double-cap) and both node-direct branches of _make_tui_argv (HERMES_TUI_DIR + built bundle), each asserting --expose-gc lands on argv before the entry script. All 4 pass.
  • Adjacent suite: tests/hermes_cli/test_tui_resume_flow.py, tests/hermes_cli/test_tui_npm_install.py, tests/hermes_cli/test_launcher.py — 17 / 17 pass on this branch.
  • Regression guard: all 4 new tests fail on clean origin/main (188eaa57c) with the exact reporter symptom: AssertionError: --expose-gc must not be injected into NODE_OPTIONS (#17187); got NODE_OPTIONS='--max-old-space-size=8192 --expose-gc'. They pass with the fix applied.

Related

…ousResearch#17187)

Since NousResearch#13231 the TUI launcher set
``NODE_OPTIONS="--max-old-space-size=8192 --expose-gc"`` to keep long
sessions from fatal-OOM. ``--expose-gc`` is a V8 flag, and node
explicitly rejects V8 flags supplied through NODE_OPTIONS:

    /usr/bin/node: --expose-gc is not allowed in NODE_OPTIONS

The user from NousResearch#17187 hits this on Node 20.12.2; ``hermes --tui`` exits
before the TUI ever loads.

Move ``--expose-gc`` out of the env var and onto the node CLI inside
``_make_tui_argv`` for the two node-direct branches (HERMES_TUI_DIR
fast path and the built-bundle fallback). ``--max-old-space-size`` is
still set in NODE_OPTIONS — that one is allowed there and the
user-supplied override semantics are preserved.

The dev paths (`tsx src/entry.tsx`, `npm start`) don't get
``--expose-gc`` because they don't invoke node directly. That's a
pragmatic trade-off: those paths are for hermes maintainers, and they
were broken on Node 20+ before this change too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 29, 2026 01:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes hermes --tui failing on Node.js ≥ 20 due to Node rejecting --expose-gc when injected via NODE_OPTIONS, by moving the flag onto the Node CLI argv for node-direct TUI launches while keeping the heap cap in NODE_OPTIONS.

Changes:

  • Update _make_tui_argv to pass --expose-gc as a Node CLI flag for the external dist (HERMES_TUI_DIR) and bundled dist launch paths.
  • Update _launch_tui to only inject --max-old-space-size=8192 into NODE_OPTIONS (and document why --expose-gc is excluded).
  • Add regression tests covering NODE_OPTIONS construction and both node-direct argv branches.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
hermes_cli/main.py Moves --expose-gc to Node argv for node-direct launches; keeps heap cap via NODE_OPTIONS.
tests/hermes_cli/test_tui_node_options_expose_gc.py Adds regression tests to ensure --expose-gc is not injected into NODE_OPTIONS and is present on argv for node-direct paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"""

from pathlib import Path
import os

Copilot AI Apr 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

os is imported but never used in this test module; please remove it to avoid unused-import noise (and potential lint failures if/when linting is enforced in CI).

Suggested change
import os

Copilot uses AI. Check for mistakes.
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/cli CLI entry point, hermes_cli/, setup wizard labels Apr 29, 2026
Copilot flagged `import os` as unused in
tests/hermes_cli/test_tui_node_options_expose_gc.py — confirmed via
`grep -n 'os\.'` (no references). Removing keeps the test module lint-clean
and matches the pattern enforced across recent test additions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briandevans

Copy link
Copy Markdown
Contributor Author

@copilot Finding addressed in 1654e59: dropped the unused import os from tests/hermes_cli/test_tui_node_options_expose_gc.py. Verified with grep -n 'os\.' — no references in the module. 4/4 regression tests still pass under uv run --with pytest --with pytest-xdist python3 -m pytest tests/hermes_cli/test_tui_node_options_expose_gc.py -v.

@briandevans

Copy link
Copy Markdown
Contributor Author

CI audit — all 34 test job failures + 1 collection error on commit 1654e59ce are pre-existing baselines on clean origin/main (5a61c116e, run 25087749551). Zero failures intersect with touched code (hermes_cli/main.py Node --expose-gc flag-vs-NODE_OPTIONS path, tests/hermes_cli/test_tui_node_options_expose_gc.py).

Same baseline cluster as #17569, #17441, #17386, #17348, #17322 — credential_sources/minimax-oauth, normalize_whatsapp_identifier collection error, mcp_structured_content _rpc_lock stubs, clipboard WSL ordering, mcp dynamic discovery, session_split_brain timeouts, web_server PTY/reload-env, etc. Most covered by open PR #17334.

The --expose-gc flag-positioning fix and its targeted regression test (test_tui_node_options_expose_gc.py, 4/4 passing) touch no baseline-failing modules.

@briandevans

Copy link
Copy Markdown
Contributor Author

Closing to keep the queue clean — happy to reopen if this is still useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: hermes --tui fails with --expose-gc is not allowed in NODE_OPTIONS

3 participants