fix(cli): emit JSON envelope for Commander parse errors under --output json#22
Open
crypticsaiyan wants to merge 1 commit into
Open
Conversation
…t json When a subcommand fired a parse error (unknown command, missing required argument, invalid option), Commander's outputError callback wrote plain text to stderr immediately and the catch block exited 5 with no further output. A machine consumer that always parses stderr as JSON received an unexpected plain-text string and crashed its JSON.parse. Root cause: configureOutput was only applied to the root program, not to subcommands. Each subcommand retained the default outputError that calls write(str) directly. applyExitOverrideDeep now also propagates configureOutput to every leaf so the message is buffered instead of written. In the CommanderError catch block, a resolved output mode is used to either write a VALIDATION_ERROR JSON envelope or the buffered plain-text message. An argv scan fallback handles the edge case where --output json appears after the bad argument and was not yet parsed when the error fired. The renderCommanderError helper is extracted to src/lib/render-error.ts (alongside the existing rephraseUnknownOption helper) so it is unit-testable without a subprocess. Eight unit tests cover JSON/text output, null fallback, message trimming, and rephrased global-flag embedding. Four subprocess regression tests in the [fix-5] block cover missing-arg, unknown subcommand, argv-fallback, and text-mode no-regression paths.
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.
Summary
When
--output jsonis set, Commander-level parse errors (e.g., unknown command, missing arg) previously exited with code5but wrote plain text tostderr. This broke machine consumers expecting structured JSON data. All other error paths already correctly emit JSON; Commander was the sole exception.The Fix
Root Cause: The
configureOutputsetting was only applied to the root program. Subcommands kept Commander's default behavior, which writes plain text immediately before theCommanderErroris thrown and the output mode is checked.Changes:
applyExitOverrideDeepnow passesconfigureOutputto all subcommands. Errors are buffered instead of written immediately.CommanderErrorcatch block now evaluates the output mode to write either aVALIDATION_ERRORJSON envelope or standard plain text.--output jsonif it appears in the command line after the invalid argument.renderCommanderErrortosrc/lib/render-error.tsfor direct unit testing without needing a subprocess.Testing & Validation
Regression Tests:
src/lib/render-error.test.tscovering JSON/text modes, null fallbacks, and envelope shapes.test/cli.subprocess.test.tscovering missing args, unknown commands,argvfallbacks, and text mode baselines.CI Gates: All local checks pass.
typecheck)Files Changed:
src/lib/render-error.tssrc/index.tssrc/lib/render-error.test.tstest/cli.subprocess.test.ts