Skip to content

feat(parser): richer, actionable error messages on bad spec input - #137

Merged
benjamineckstein merged 2 commits into
mainfrom
feat/richer-input-errors
Jun 13, 2026
Merged

feat(parser): richer, actionable error messages on bad spec input#137
benjamineckstein merged 2 commits into
mainfrom
feat/richer-input-errors

Conversation

@benjamineckstein

Copy link
Copy Markdown
Contributor

What

Richer, more actionable error messages when the generator is handed bad INPUT (a malformed spec). Closes the "Richer error messages on bad input" ROADMAP follow-up. This touches only the generator's own error text and the context plumbing behind it; it does NOT change any generated output (confirmed: only parser source + parser tests changed, all output gates byte-identical).

Scope

The config loader (StandaloneConfigLoader) and CLI (StandaloneApplication, OptionValidator) messages were already excellent (they name the offending key/option, allowed values, and hints), so they were left untouched. The parser was the weakest surface and the focus here.

Before / after (parser messages enriched)

  • Missing/non-string openapi: now names the found type and the swagger key hint, pointing at #/openapi.
  • Unsupported version: now ... at #/openapi.
  • Missing/mistyped info: now the required '#/info' object is missing. Add an 'info' object with at least a 'title' and a 'version'.
  • Depth bound: now names the source file and the #/components/schemas/.../... pointer where the bound tripped, plus how to raise it.
  • Node-count guard (Guard parser against YAML anchor/alias amplification (billion-laughs) #107) and size guard: now name the source file and pointer / accepted flags.
  • Decode failure: now names whether JSON or YAML was attempted and how the format is detected.

How

A $pointer parameter (RFC 6901, ~// escaped) is threaded from read() through component schemas and every subschema position (properties, items, additionalProperties, allOf/oneOf/anyOf/not, prefixItems). It degrades to an empty pointer (no at ... suffix) for positions the reader does not pinpoint, so a message never shows a misleading partial pointer. Exit codes and exception types are unchanged (ParseException, config/spec errors still exit 2).

Tests

Extended OpenApiReaderTest and SpecParserTest to assert the new context (pointer, escaped pointer, expected-vs-found type, hints, source filename, attempted format) so the messages cannot silently regress.

Gates (local, all green)

composer test 2185 passed / 5 skipped, test:type 100%, stan clean, Pint clean, deptrac 0. No generated-output gate (corpus baseline, ServerScaffold snapshot, drift, conformance golden) changed.

…rrors

Thread an RFC 6901 JSON pointer through schema hydration so the
depth-bound (#107 sibling) and node-count guards name WHERE in the
document the offending node sits, e.g.
'at #/components/schemas/Deep/properties/next/...'. The pointer is
escaped (~ -> ~0, / -> ~1) and degrades to no-location cleanly when the
reader cannot pinpoint the node. The structural rejections (missing or
mistyped openapi version, missing or mistyped info) now state the
pointer, the expected-vs-found shape (a string, a number, an object,
missing), and an actionable hint. The depth-bound and node-count
messages also name the source file and point at --max-depth / max_depth.
The pointer only ever appears in exception text, never in generated
output.
…errors

The decode-failure wrapper now names whether the file was parsed as JSON
or YAML (taken from the extension, falling back to the first-byte sniff)
and adds a hint about that detection, so a YAML file with a .json
extension no longer produces an opaque 'Failed to parse' message. The
size-guard message names the offending file and points at both the
--max-bytes flag and the max_bytes config key. Output text only; no
generated output changes.
@github-actions

Copy link
Copy Markdown
Contributor

Qodana for PHP

It seems all right 👌

No new problems were found according to the checks applied

💡 Qodana analysis was run in the pull request mode: only the changed files were checked
☁️ View the detailed Qodana report

Detected 12 dependencies

Third-party software list

This page lists the third-party software dependencies used in project

Dependency Version Licenses
doctrine/deprecations 1.1.6 MIT
phpdocumentor/reflection-common 2.2.0 MIT
phpdocumentor/reflection-docblock 6.0.3 MIT
phpdocumentor/type-resolver 2.0.0 MIT
phpstan/phpdoc-parser 2.3.2 MIT
spatie/laravel-data 4.23.0 MIT
spatie/laravel-package-tools 1.93.1 MIT
spatie/php-structure-discoverer 2.4.2 MIT
symfony/finder v8.1.0 MIT
symfony/polyfill-ctype v1.37.0 MIT
symfony/yaml v8.1.0 MIT
webmozart/assert 2.4.0 MIT
Contact Qodana team

Contact us at qodana-support@jetbrains.com

@benjamineckstein
benjamineckstein merged commit c0cb2b6 into main Jun 13, 2026
14 checks passed
@benjamineckstein
benjamineckstein deleted the feat/richer-input-errors branch June 15, 2026 05:59
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