Skip to content

Fix runRegula() hang on empty paths, WASI version stamping; release v3.2.6 - #5

Merged
NikhilVerma merged 3 commits into
masterfrom
fix/stdin-hang
Jul 20, 2026
Merged

Fix runRegula() hang on empty paths, WASI version stamping; release v3.2.6#5
NikhilVerma merged 3 commits into
masterfrom
fix/stdin-hang

Conversation

@NikhilVerma

Copy link
Copy Markdown

Summary

Two bugs, plus a reconciliation of master with what is actually published on npm.

runRegula() never resolves when called with an empty paths array. The promise neither resolves nor rejects:

await runRegula([], { include: [rego], inputType: "tf", noBuiltIns: true });
// -> never settles (observed >25s, unbounded)

await runRegula([someTf], { include: [rego], inputType: "tf", noBuiltIns: true });
// -> resolves in ~1.1s

Mechanism:

  1. With no file arguments the regula CLI falls back to reading stdin — conventional CLI behaviour.
  2. index.js invokes the CLI via execFile("node", [cliPath, ...args]), which gives the child an open stdin pipe.
  3. Nothing writes to or closes that pipe, so the WASI process blocks on read forever.

Isolated by running the CLI directly: identical invocation exits 0 with < /dev/null and times out (124) with an open pipe.

Impact is narrow — only the empty-paths call path. Passing input files is unaffected, so most callers never hit it. The symptom is misleading though: on the direct-CLI path the process prints its results and then hangs, so it reads as a slow evaluation rather than a stdin deadlock.

build-wasi.sh did not stamp version ldflags. The WASI build reported unknown-version, build unknown-commit while the native binary reported real values — the sole failure in the WASI parity suite.

On the fix

The two obvious fixes do not work, and both fail silently:

Attempt Result
input: "" still hangs — input is an execFileSync option; async execFile discards it
stdio: ["ignore","pipe","pipe"] still hangs — execFile overrides stdio to own the callback's pipes

Verified the second by probing: after passing stdio: ["ignore", …], child.stdin !== null is still true. The working fix is to close the stream on the returned handle:

const child = execFile("node", [cliPath, ...args], {}, cb);
child.stdin.end();

Settles in ~1.35s, exit 0, resolving with an empty result set rather than rejecting.

Version

v3.2.5 is published on npm but was never merged to master (it lived only on origin/add-typescript-types), which is why master still read 3.2.4. This PR brings that commit along, adds the changelog entry it shipped without, and bumps to v3.2.6.

Testing

  • WASI parity suite: 10/10 passed (was 9/10 — version stamping was the failure)
  • Main suite: 7/7 passed, including a new regression test
  • The regression test is guarded by an explicit 30s timeout, since a bare await would hang the suite instead of reporting a failure. Confirmed it fails when the fix is reverted (runRegula([]) did not settle within 30s).

Also included

  • test/test-wasi.js honours WASI_RUNTIME (default node) instead of hardcoding node, so the parity suite can target another runtime. Without this, running the suite under a different runtime silently still shells out to node and reports a false pass.
  • vendor/ added to .gitignore — it is generated by build-wasi.sh via go mod vendor but was showing up as untracked.

Not addressed here

Two separate issues found while investigating, deliberately left out to keep this focused:

  • Running the CLI directly with < /dev/null exits 0 but emits FATAL rules/tf/aws/cloudtrail/s3_access_logging.rego:42: eval_conflict_error: object keys must be unique with zero bytes of stdout (reproducible 3/3). The fixed path does not hit this.
  • Bun's node:wasi cannot run any Go-compiled wasip1 module — reproduced with a three-line fmt.Println binary. Bun lacks getImportObject() and sock_accept, and past those it faults with an out-of-bounds memory access inside its own start(). Not fixable from this repo; cli.js already has a #!/usr/bin/env node shebang so bunx regula still works.

🤖 Generated with Claude Code

NikhilVerma and others added 2 commits February 5, 2026 11:56
- Add RegulaError class with stdout, stderr, exitCode, and command properties
- Remove console.error call (let caller handle stderr)
- Treat exit code 1 as success (expected for security violations)
- Update TypeScript definitions for RegulaError
- Document error handling in README
- Bump version to 3.2.5

Fixes: stderr from OPA errors now accessible to callers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
runRegula(paths, opts) never settled when called with an empty paths array —
the promise neither resolved nor rejected. With no path arguments the regula
CLI falls back to reading stdin, and execFile always hands the child an open
stdin pipe, so the WASI process blocked on read indefinitely. Passing any
input path was unaffected, which is why this went unnoticed.

The child's stdin is now closed explicitly. Note that the two obvious fixes
do not work here: execFile ignores the `stdio` option because it owns the
pipes used to build the callback's stdout/stderr, and `input` is an
execFileSync-only option that async execFile silently discards. Both leave
the hang in place.

Also stamps version.Version and version.GitCommit in build-wasi.sh, matching
the Makefile. The WASI build previously reported "unknown-version, build
unknown-commit" while the native binary reported real values, which was the
sole failure in the WASI parity suite.

Adds a regression test guarded by an explicit 30s timeout — a bare await
would hang the suite rather than report a failure if this regresses. Verified
it fails when the fix is reverted.

Documents v3.2.5 (published to npm but never merged to master, and shipped
without a changelog entry) and bumps to v3.2.6.

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

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0a8e2f67-c142-4ce4-beeb-783459af45a3

📥 Commits

Reviewing files that changed from the base of the PR and between 8bafd7c and e2559e3.

📒 Files selected for processing (11)
  • .gitignore
  • CHANGELOG.md
  • README.md
  • build-wasi.sh
  • changes/v3.2.5.md
  • changes/v3.2.6.md
  • index.d.ts
  • index.js
  • package.json
  • test/test-wasi.js
  • test/test.js

Comment @coderabbitai help to get the list of available commands.

CHANGELOG.md had not been updated since v3.2.1 (2023-02-16). Five releases
were published to npm without ever landing here — 3.2.2, 3.2.3, 3.2.4 and
3.2.5 are all live on the registry with no entry in the file anyone actually
reads.

The changes/*.md files feed CHANGELOG.md via `changie merge`, which was never
run for these releases (and changie is not installed in this environment), so
even v3.2.4, which did have a changes/ file, was missing from the changelog.

Entries for 3.2.2 and 3.2.3 are reconstructed from git history, since neither
release left a changes/ file behind.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NikhilVerma
NikhilVerma merged commit 21f70df into master Jul 20, 2026
5 of 7 checks passed
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.

1 participant