[BUG] `asyncapi diff` prints empty `ValidationError:` on parse failure
- Dominant language
- TypeScript
- Stars
- 272
- Forks
- 375
- Avg merge
- 3h 22m
- Merged PRs (30d)
- 8
Description
## Symptom
When `asyncapi diff` is invoked with two documents and one of them fails to parse, the command exits with code `1` and prints a literal `ValidationError:` followed by no diagnostic body:
```
$ asyncapi diff old.yaml broken.yaml
...
ValidationError:
$ echo $?
1
```
This makes it impossible for users (and CI logs) to know why parsing failed.
## Reproduction
Repo is reproducible against `master` using the fixtures shipped in the repo:
```
$ git clone https://github.com/asyncapi/cli && cd cli
$ npm ci && npm run build
$ ./bin/run_bin diff test/fixtures/specification.yml test/fixtures/specification-invalid.yml
...
ValidationError:
$ echo $?
1
```
Confirmed on `master` (current 6.x) and v5.0.5.
## Root cause
Two contributing bugs in the parser-failure path:
1. **Wrong ValidationError type for parse failures** - [`src/apps/cli/commands/diff.ts`](https://github.com/asyncapi/cli/blob/master/src/apps/cli/commands/diff.ts) `parseDocuments` constructs:
```typescript
new ValidationError({
type: 'invalid-file',
filepath: firstDocument.getFilePath() || secondDocument.getFilePath(),
err: firstResult.error || secondResult.error,
})
```
`'invalid-file'` is the file-not-found template; it renders `There is no file or context with name "".` and ignores the `err:` field entirely. The other parse-failure path in the same file (the catch block at the bottom of `run()`) already uses `'parser-error'` correctly, so this is an internal inconsistency rather than a design choice.
2. **`buildError` emits an empty string for non-ParserError shapes** - [`src/errors/validation-error.ts`](https://github.com/asyncapi/cli/blob/master/src/errors/validation-error.ts) `buildError(err)` only inspects `err.title`, `err.detail`, and `err.validationErrors`. The current `ValidationService.parseDocument` returns its error as a plain string (`'Failed to parse document'`), so none of those checks match and `this.message` stays `''`. Even with fix #1 applied, the outer `catch` in `run()` wraps the original `ValidationError` in another one with `type: 'parser-error'`, and `buildError` once again sees no `title` / `detail` / `validationErrors` - so the message is still empty by the time oclif prints it.
The combination of these two bugs is what produces the bare `ValidationError:` symptom.
## Related prior art
- #2093 - `BUG: Diff command doesn't work when circular reference`. Different code path (circular `$ref` throws a `TypeError` during JSON serialisation rather than failing the parser's `success` check), but same general class: parse-time failures aren't surfaced gracefully by `diff`.
- #2113 / #2167 - earlier PRs attempting to handle the circular-ref case. Closed without merge; maintainers asked for tests.
## Proposed fix
I have a PR ready that:
1. Switches the `parseDocuments` failure branch from `'invalid-file'` to `'parser-error'`, mirroring the existing usage in the `run()` catch block.
2. Adds a fallback in `buildError` so any error shape (string, generic `Error`, or undefined) produces a non-empty message instead of `''`.
3. Adds a unit test for `ValidationError` (no test file exists today) and an integration test for `diff` against the parse-failure path.
I'll open it shortly and link it here.
## Environment
- `@asyncapi/cli` master (current 6.x) and v5.0.5
- Node 24, npm 11
- macOS, but the bug is platform-independent (lives in CLI-side error routing)
Contributor guide
Assessment
This issue has not been assessed yet.