Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Improve error rendering and add required-args guardrails#2

Merged
johnpmitsch merged 3 commits into
mainquicknode/cli:mainfrom
error_handlingquicknode/cli:error_handlingCopy head branch name to clipboard
Jun 5, 2026
Merged

Improve error rendering and add required-args guardrails#2
johnpmitsch merged 3 commits into
mainquicknode/cli:mainfrom
error_handlingquicknode/cli:error_handlingCopy head branch name to clipboard

Conversation

@johnpmitsch
Copy link
Copy Markdown
Collaborator

Summary

Today, bad input to qn produces opaque output. A 400 from the API gets rendered as Error: API returned HTTP 400. with the structured validation body hidden behind --verbose; an endpoint create with zero flags returns a 400 with an empty body, leaving the user with no signal at all.

This PR is two stages:

Stage A — universal error-body parsing in src/errors.rs. Walks JSON 400 bodies for any of error, errors, message, messages (strings, arrays, nested objects — covers NestJS, JSON:API, admin-wrapper, and ad-hoc shapes). Renders one bullet per extracted message under a clean Error: invalid request. headline. When a bullet matches X must be one of …, runs Levenshtein against the user's argv and appends did you mean '…'? if the best match is within distance 3 and shares a 3-char prefix. Truncates long enum lists to 5 + (N more). Falls back to the raw body when JSON parsing fails, and --verbose still dumps everything.

Stage B — required-args pre-flight on four commands. Where the server returns an empty 400 because the user gave nothing to act on, fail fast client-side with a clear message instead. Covers endpoint create, endpoint update, endpoint security set-options, and team set-endpoints.

Before / after

# Before
$ qn webhook create --network sfjla …
Error: API returned HTTP 400.

# After
$ qn webhook create --network sfjla …
Error: invalid request.
  • network must be one of: ethereum-mainnet, ethereum-sepolia, … (108 more)
Run 'qn chain list' to see supported networks.
Re-run with --verbose for the full response body.

# After (close-typo case fires did-you-mean)
$ qn webhook create --network ethereum-mainnetsds …
  • network must be one of: … — did you mean 'ethereum-mainnet' (you passed 'ethereum-mainnetsds')?

# Before
$ qn endpoint create
Error: API returned HTTP 400.

# After
$ qn endpoint create
Error: invalid argument: 'endpoint create' requires at least one of --chain or --network. Run 'qn chain list' to see available chains.

Test plan

  • cargo test — 172 passing, including 17 new unit tests in errors and 6 new integration tests (tests/webhooks.rs, tests/endpoint.rs, tests/admin_extra.rs)
  • cargo clippy --all-targets -- -D warnings clean
  • cargo fmt --check clean
  • Manual smoke against the real API for the three failing commands in the original report

The CLI previously rendered 400 responses as 'API returned HTTP 400.'
with the raw JSON dump hidden behind --verbose. The server already
returns structured validation errors; this surfaces them.

errors::render_api_error now:
- Walks JSON bodies for error/errors/message/messages keys, accepting
  strings, arrays of strings, arrays of objects, and nested objects.
- Treats 400/422 as 'invalid request.' with one bullet per extracted
  message.
- For 'X must be one of: …' bullets, runs Levenshtein against argv and
  appends a did-you-mean when the best match is within distance 3 and
  shares a 3-char prefix.
- Truncates long enum lists to 5 + '(N more)'.
- Maps known field names (network, chain) to follow-up commands.
- Falls back to printing the raw body when JSON parsing fails.
- Verbose still dumps the full body.

Adds render_with_argv so the integration-test harness can use the
simulated argv instead of the test binary's process argv. 17 new unit
tests + 2 wiremock cases in tests/webhooks.rs.
Closes the 'empty-body 400' gap audited across the create/update
surface: where the user supplies zero (or trivially insufficient)
flags, the server today returns a 400 with no useful body — the
new Stage A renderer can't help. These commands now fail fast
client-side with a CliError::Arg naming the missing flags.

- endpoint create: require at least --chain or --network
- endpoint update: require --label
- endpoint security set-options: require at least one toggle
- team set-endpoints: require at least one endpoint id

webhook create's required fields are already enforced by clap.
Four integration tests assert exit code 1 and zero HTTP traffic.
The server requires both fields, not 'at least one'. Tighten the
pre-flight to name whichever flag (or both) the user omitted instead
of letting --chain-only through to a 400.
@johnpmitsch johnpmitsch merged commit df22268 into main Jun 5, 2026
8 checks passed
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.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.