Extend friendly API errors to 409 conflict + 422 validation (#123) #163

Merged
stephen merged 1 commit from fix/123-extend-friendly-errors-409-422 into main 2026-06-15 15:15:48 +00:00
Owner

#123 was resolved by #142, which gave 401/403/404/429 a short human-readable headline and pushed the raw /api/v1 URL down to the caused by: source. This extends that treatment to the two client-error classes #142 left as raw dumps.

Before, 409 and 422 still led with the internal URL because friendly_http_error returned None for them:

error: HTTP 409 Conflict from https://rasterhub.com/api/v1/repos/o/r/branches: The branch already exists.

What

  • friendly_http_error now handles CONFLICT (409) and UNPROCESSABLE_ENTITY (422). For these, the server's own message is the actionable detail (a conflict reason, a validation error), so the headline surfaces that instead of the URL: conflict: o/r: The branch already exists. / invalid request: o/r: head and base are the same. The message is length-capped via truncate_preview so a verbose validation body can't blow up the headline. The full ApiError (with the URL) stays as the --debug / FJ_DEBUG=1 caused by: source, same as the other classes.
  • Exit code is intentionally unchanged: 409 and 422 keep the generic 1. They are request problems, not the not-found / auth / transient classes that carry 3/4/5, so a script can't meaningfully branch on them the way it does for those.
  • Tests for the 409 and 422 headlines + exit code, mirroring the existing maps_4xx tests; docs/exit-codes.md updated to list 409/422 and note they stay exit 1.

Resolves the remaining gap in #123. cargo fmt --check, cargo clippy --all-targets, and the full test suite pass locally.

#123 was resolved by #142, which gave 401/403/404/429 a short human-readable headline and pushed the raw `/api/v1` URL down to the `caused by:` source. This extends that treatment to the two client-error classes #142 left as raw dumps. Before, 409 and 422 still led with the internal URL because `friendly_http_error` returned `None` for them: ``` error: HTTP 409 Conflict from https://rasterhub.com/api/v1/repos/o/r/branches: The branch already exists. ``` ## What - `friendly_http_error` now handles `CONFLICT` (409) and `UNPROCESSABLE_ENTITY` (422). For these, the server's own message is the actionable detail (a conflict reason, a validation error), so the headline surfaces that instead of the URL: `conflict: o/r: The branch already exists.` / `invalid request: o/r: head and base are the same`. The message is length-capped via `truncate_preview` so a verbose validation body can't blow up the headline. The full `ApiError` (with the URL) stays as the `--debug` / `FJ_DEBUG=1` `caused by:` source, same as the other classes. - Exit code is intentionally unchanged: 409 and 422 keep the generic `1`. They are request problems, not the not-found / auth / transient classes that carry 3/4/5, so a script can't meaningfully branch on them the way it does for those. - Tests for the 409 and 422 headlines + exit code, mirroring the existing `maps_4xx` tests; `docs/exit-codes.md` updated to list 409/422 and note they stay exit `1`. Resolves the remaining gap in #123. `cargo fmt --check`, `cargo clippy --all-targets`, and the full test suite pass locally.
Extend friendly API errors to 409 conflict + 422 validation (#123)
All checks were successful
ci / check (pull_request) Successful in 9m51s
ci / live-e2e (pull_request) Successful in 1m50s
ci / coverage (pull_request) Successful in 2m2s
3e32597d81
#123 was resolved by #142, which gave 401/403/404/429 a human headline and
hid the raw /api/v1 URL behind the `caused by:` source. This extends that to
the two remaining client-error classes #142 left as raw dumps: 409 and 422
still printed `HTTP 409/422 ... from https://.../api/v1/...` because
friendly_http_error returned None for them.

- friendly_http_error now handles CONFLICT and UNPROCESSABLE_ENTITY, surfacing
  the server's own message (the actionable detail for these: "branch already
  exists", a validation error) in the headline instead of the URL, length-
  capped via truncate_preview. The full ApiError (with the URL) stays as the
  --debug `caused by:` source.
- Exit code is unchanged for these: 409/422 keep the generic 1, since they are
  request problems, not the not-found/auth/transient classes that carry 3/4/5.
- Tests for the 409 and 422 headlines + exit code; docs/exit-codes.md updated.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rasterstate/fj!163
No description provided.