Skip to content

fix: use parseAsync to propagate async handler errors to exit code#46

Merged
nazarhussain merged 2 commits intoChainSafe:mainfrom
lodekeeper:fix/cli-async-exit-code
Mar 30, 2026
Merged

fix: use parseAsync to propagate async handler errors to exit code#46
nazarhussain merged 2 commits intoChainSafe:mainfrom
lodekeeper:fix/cli-async-exit-code

Conversation

@lodekeeper
Copy link
Copy Markdown

Problem

The CLI entry point uses void yargs(...).parse() which discards the Promise from async command handlers. When a benchmark run throws (e.g. all benchmarks fail), the rejection is unhandled and the process exits with code 0 instead of code 1.

The .fail() handler exists and calls process.exit(1), but with synchronous .parse() the async rejection never reaches it.

Reproduced locally:

  ✖️ forkChoice updateHead vc 100000 bc 64 eq 0
Error: blockRoot vote1 == vote2
  ...
  9 failed
Error processing benchmark files. No benchmark result was produced
 ✖ Error: No benchmark result was produced

EXIT CODE: 0  ← should be 1

Fix

Replace void yargs(...).parse() with .parseAsync() so the async handler's rejection is properly caught by yargs and routed to the .fail() handler.

The .catch() on parseAsync() prevents an unhandled rejection warning since the .fail() handler already calls process.exit(1) before the promise settles.

Ref: ChainSafe/lodestar#7484

The CLI used `void yargs(...).parse()` which discards the Promise from
async command handlers. When a benchmark run throws (e.g. all benchmarks
fail), the rejection is unhandled and the process exits with code 0
instead of 1.

Switch to `.parseAsync()` so the async handler's rejection is properly
caught by yargs and routed to the `.fail()` handler which calls
`process.exit(1)`.

The `.catch()` on parseAsync prevents an unhandled rejection warning
since the `.fail()` handler already calls `process.exit(1)`.

Ref: ChainSafe/lodestar#7484
@nflaig nflaig requested a review from nazarhussain March 28, 2026 15:33
Wrap postGaComment in try-catch so that HttpError (e.g. fork PRs lack
pull-requests:write permission) logs a warning instead of propagating
as a fatal error. The benchmark results themselves are unaffected.

This is the same fix applied in PR ChainSafe#45 (fix/fail-on-benchmark-errors).
Copy link
Copy Markdown

@nazarhussain nazarhussain left a comment

Choose a reason for hiding this comment

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

@lodekeeper void is just syntax sugar from TS, it does not impact on behavior of promise. Actual change is the replacing parse with parseAsync.

@nazarhussain nazarhussain merged commit 9656662 into ChainSafe:main Mar 30, 2026
8 checks passed
@lodekeeper
Copy link
Copy Markdown
Author

Good point, thanks for the correction @nazarhussain! You're right — void is purely TypeScript syntax sugar and has no runtime effect on promise behavior. The actual fix is parseAsync which makes yargs await the async handler and propagate errors to the exit code. I'll update the PR description to be more accurate.

This was referenced Mar 30, 2026
nflaig pushed a commit to ChainSafe/lodestar that referenced this pull request Mar 30, 2026
## Motivation

Bumps `@chainsafe/benchmark` from `1.2.3` to `2.0.2`, which includes
fixes for error handling that were silently swallowing benchmark
failures.

### Fixes included in 2.0.x

- **[PR #45](ChainSafe/benchmark#45 —
Benchmark failures are now logged to stderr and cause CI to exit
non-zero instead of being silently dropped
- **[PR #46](ChainSafe/benchmark#46 — CLI
uses `parseAsync()` to properly propagate async handler errors to exit
code

### Verification

Ran fork-choice benchmarks locally to confirm they pass with the new
version:
- `computeDeltas` — 8/8 passing
- `forkChoice/updateHead` — 9/9 passing
- `forkChoice/onAttestation` — 1/1 passing
- `utils/bytes` — 20/20 passing

Resolves #7484

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants