Repository navigation
fix(validate-utils): write the error report to stderr synchronously so process.exit cannot truncate it - #1293
Merged
Conversation
reportAndExit() printed every error with console.error() and then called process.exit(1). Node documents stderr writes as asynchronous on POSIX when stderr is a pipe, and process.exit() does not wait for pending writes, so the tail of a long validator report can be dropped before the parent reads it -- the shape of the merge_group failure on run 38079181421, where the 18 missing lines were the alphabetical tail of one report. Build each report into a single string and write it to stderr's file descriptor with fs.writeSync(), which is synchronous whatever stderr is connected to, resuming short writes and retrying EAGAIN. Warnings share the same path so a report carrying both severities cannot lose its warning half to the exit below, and the two halves cannot interleave. process.exitCode = 1 would not work here: every validator prints a "Validated N ..." success line immediately after reportAndExit(), which must stay unreachable once errors have been reported. A new test pins that. Closes #1286 Co-authored-by: mrbobbytables <6500863+mrbobbytables@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality <quality@hive.kubestellar.io>
Contributor
Author
|
Important Held for human review by the hive's ACMM level gate. This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the Hive will keep the |
This was referenced Oct 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Test Improvement
reportAndExit()inscripts/lib/validate-utils.mjsprinted every error withconsole.error()and then calledprocess.exit(1). Node documents stderrwrites as asynchronous on POSIX when stderr is a pipe, and
process.exit()does not wait for pending writes, so the tail of a long validator report can be
dropped before the parent reads it. All 12
validate-*.mjsscripts reach thispath, and
tests/e2e-data-fixture-validity.test.mjscompares a validator's fullpiped output against a memoized baseline, so a truncated baseline reports the
lost lines as new errors.
Change
reportAndExit()now builds each report into one string and writes it tostderr's file descriptor with
fs.writeSync(), which is synchronous whateverstderr is connected to. Short writes are resumed and
EAGAINis retried.cannot lose its warning half to the exit below and the two halves cannot
interleave.
Why not
process.exitCode = 1The alternative suggested in the issue thread is not safe here, and I verified
why before discarding it: every validator prints a
Validated N ...successline immediately after its top-level
reportAndExit()call(
validate-members.mjs:433,validate-architecture-assets.mjs:419, and tenmore). Letting the script fall through would print a success line after a failed
run.
tests/validate-utils.test.mjsnow pins that invariant, so the incorrectfix cannot be reintroduced silently.
Tests
tests/validate-utils.test.mjscapturesfs.writeSyncinstead ofconsole.error/console.warn, and adds:console;EAGAINis retried, and a non-EAGAINfailure propagates;reportAndExit()prints a success line afterwards.Verification at this head:
npm run test:unit:coverage:checkpasses with 2324tests green;
scripts/lib/validate-utils.mjsandtests/validate-utils.test.mjsare both at 100.00% lines and 100.00% regions, and the
src filestotals stay at100.00/100.00. Prettier is clean on both files.
One honest caveat: the piped-report test passes against the old
console.errorimplementation too in this environment — the truncation isload-dependent, which is why it surfaced in a merge_group runner and not
locally. The deterministic pins are the fd-write, resume and retry tests; the
piped test guards the end-to-end contract.
Related Issue
Closes #1286
Human review required.
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88 requested_by=@mrbobbytables