Skip to content

CLI: one error: line for a flag with no path, an invalid schema, or a $ref that does not resolve - #882

Draft
claude[bot] wants to merge 3 commits into
masterfrom
claude/cli-user-errors-no-stack-47rly1
Draft

claude[bot] wants to merge 3 commits into
masterfrom
claude/cli-user-errors-no-stack-47rly1

Conversation

@claude

@claude claude Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Three mistakes a person can make at the command line still printed an internal stack trace, after #876 made the CLI's own usage errors one error: line. Each is now one plain line saying what to fix. Exit codes are unchanged (1) and generated output is untouched.

Before / after

A path flag with nothing after it (-i, -o, --output=):

$ json2ts schema.json -o
error: TypeError [ERR_INVALID_ARG_TYPE]: The "paths[1]" argument must be of type string. Received type boolean (true)
    at resolve (node:path:1272:7)
    at /…/dist/src/cli.js:196:107
    … 8 more frames
$ json2ts schema.json -o
error: --output (-o) needs a path after it, e.g. -o schema.d.ts

An invalid schema (compile() already prints one line per rule it breaks; the CLI then printed the empty ValidationError it throws):

$ echo '{"type":"array","minItems":-1}' | json2ts
error: Error at key "null" in file "NoName": When minItems exists, minItems >= 0
error: ValidationError
    at /…/dist/src/index.js:176:19
    at Generator.next (<anonymous>)
    at fulfilled (/…/dist/src/index.js:5:58)
$ echo '{"type":"array","minItems":-1}' | json2ts
error: Error at key "null" in file "NoName": When minItems exists, minItems >= 0

A $ref that does not resolve (a missing or unparsable file, a pointer to nothing; json-schema-ref-parser's error was printed whole, with its stack and fields):

$ json2ts schema.json      # {"properties":{"x":{"$ref":"./nothere.json"}}}
error: ResolverError: Error opening file /…/nothere.json: ENOENT: no such file or directory, open '/…/nothere.json'
    at Object.read (file:///…/json-schema-ref-parser/dist/lib/resolvers/file.js:40:19) {
  source: '/…/nothere.json',
  path: null,
  code: 'ERESOLVER',
  toJSON: [Function: bound toJSON],
  ioErrorCode: 'ENOENT'
}
$ json2ts schema.json
error: Error opening file /…/nothere.json: ENOENT: no such file or directory, open '/…/nothere.json'

When the resolver's message does not name the file the $ref sits in (a pointer into another file that has no such definition), the line says which file: error: Missing $ref pointer "#/definitions/nope". Token "nope" does not exist. (in /…/other.json). A pointer into the schema itself gets no suffix (the resolver's source for it is just the working directory). With --$refOptions.continueOnError, every collected error gets its own line.

Also: invalid JSON on standard input said Error parsing JSON in file "null"; it now says Error parsing JSON from standard input.

What is deliberately unchanged

How

src/cli.ts: --input/--output given as boolean true (nothing after the flag) or '' is a UsageError, checked before the value reaches isDynamicPattern or path.resolve. The catch block prints e.message for a UsageError, one message per error for json-schema-ref-parser's JSONParserError / JSONParserErrorGroup, nothing more for a ValidationError, and the error itself (with stack) for anything else. src/utils.ts: the stdin parse message.

Tests

Eight new cliFailTests in test/cli.test.ts (-o at the end of the line, --output=, -i alone, invalid schema on stdin, invalid JSON on stdin, a $ref to a missing file, a $ref to a missing pointer in another file, a $ref to a missing pointer in the schema itself); each asserts exit 1, the message, and where it applies that stderr is one line with no at frame. All fail on master. CI=true bun run test: 688 pass, lint, format and typecheck clean.

… a `$ref` that does not resolve

Three user mistakes still printed an internal stack trace after the CLI's own
usage errors became one line:

- `json2ts schema.json -o` / `json2ts -i` (and `--output=`): the flag's value
  was boolean `true` (or ''), which reached path.resolve() as a TypeError with a
  stack. Now: `error: --output (-o) needs a path after it, e.g. -o schema.d.ts`.
- An invalid schema: compile() prints one line per rule the schema breaks, then
  the CLI printed the ValidationError it throws (empty message, three frames).
  Now only the rule lines are printed.
- A `$ref` json-schema-ref-parser cannot resolve (a missing or unparsable file,
  a pointer to nothing): its error was printed with the stack and its fields.
  Now the message alone, one line per error, with `(in <file>)` appended when
  the message does not already name the file the `$ref` was found in.

Invalid JSON on stdin also said `in file "null"`; it now says `from standard
input`. Exit codes are unchanged (1) and generated output is untouched.
claude Bot pushed a commit that referenced this pull request Sep 7, 2026
…chema, or an unresolved $ref) into the UserError branch

Both change main()'s catch block and parseFileAsJSONSchema's stdin wording. The catch now prints a UserError's message alone, one line per json-schema-ref-parser error, nothing more for a ValidationError, and the error with its stack otherwise; the --input/--output guard reads the flag's values as a list, which covers #882's empty-value case and this branch's repeated-flag case.
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