Skip to content

feat(core): let a caller read an endpoint's rejection body - #1804

Draft
sileht wants to merge 1 commit into
devs/sileht/mrgfy-8703-cli-auth-commands/store-mergify-credential-os-keychain--bd91cd6dfrom
devs/sileht/mrgfy-8703-cli-auth-commands/let-caller-read-endpoint-s-rejection-body--9339bdfb
Draft

feat(core): let a caller read an endpoint's rejection body#1804
sileht wants to merge 1 commit into
devs/sileht/mrgfy-8703-cli-auth-commands/store-mergify-credential-os-keychain--bd91cd6dfrom
devs/sileht/mrgfy-8703-cli-auth-commands/let-caller-read-endpoint-s-rejection-body--9339bdfb

Conversation

@sileht

@sileht sileht commented Sep 4, 2026

Copy link
Copy Markdown
Member

Every verb on the HTTP client renders a non-2xx response into a
CliError and drops the body. That is right for an API where a
rejection is a failure, and wrong for one where it is an answer: the
OAuth device grant replies 400 {"error": "authorization_pending"} to
every poll until its owner approves in a browser, so a client that could
only see "the request failed" could never complete the flow. GET /v1/user has the same shape from the other side -- a refused credential
(403) and a deployment too old to serve the route (404) are different
sentences to the user, and both differ from a network failure.

post_form and get_outcome return an ApiOutcome: the decoded body
on success, and the status plus the decoded error body on a terminal
rejection. Retries are untouched -- 5xx and rate limits are retried
before anything is handed back -- and a rejection whose body does not
deserialize as the protocol's error type stays a plain CliError, so a
proxy's HTML 502 page is as diagnosable as it is on every other verb.

Inside the driver, tolerate_not_found: bool becomes a three-valued
OnTerminalError, because the third case is not a boolean and a third
bool would have tripped clippy::fn_params_excessive_bools. The one
ordering change it forces: the retry decision is now taken before the
error body is read, since rendering the message consumes the very bytes
the caller asked for.

post_form_no_response covers the revocation endpoint, which answers
200 with an empty body. Form encoding needs reqwest's form feature;
the device grant is the only thing in the CLI that sends one.

Fixes MRGFY-8703

Depends-On: #1803

@sileht

sileht commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 refactor(core): split Mergify and GitHub token resolution #1802
2 feat(core): store the Mergify credential in the OS keychain #1803
3 feat(core): let a caller read an endpoint's rejection body #1804 👈
4 feat(auth): the device authorization grant, client side #1805
5 feat(cli): mergify auth login, logout and status #1806
6 feat(core): use the stored credential, deprecate GITHUB_TOKEN #1807
7 fix(core): never send a Mergify user token to GitHub #1808

@mergify

mergify Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🔴 3 of 7 protections blocking · waiting on 👀 reviews and ⛓️ dependency

Protection Waiting on
🔴 ⛓️ Depends-On Requirements ⛓️ dependency
🔴 👀 Review Requirements 👀 reviews
🔴 🔎 Reviews 👀 reviews
🟢 🤖 Continuous Integration
🟢 Enforce conventional commit
🟢 📕 PR description
🟢 🚦 Auto-queue

🔴 ⛓️ Depends-On Requirements

Waiting for

This rule is failing.

Requirement based on the presence of Depends-On in the body of the pull request

🔴 👀 Review Requirements

Waiting for

  • #approved-reviews-by>=2
This rule is failing.
  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🔴 🔎 Reviews

Waiting for

  • #review-requested = 0
This rule is failing.
  • #review-requested = 0
  • #changes-requested-reviews-by = 0
  • #review-threads-unresolved = 0

Show 4 satisfied protections

🟢 🤖 Continuous Integration

  • all of:
    • check-success=ci-gate

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|internal|docs|style|refactor|perf|test|build|ci|chore|revert|ui)(?:\(.+\))?!?:

🟢 📕 PR description

  • body ~= (?ms:.{48,})

🟢 🚦 Auto-queue

When all merge protections are satisfied, this pull request will be queued automatically.

Every verb on the HTTP client renders a non-2xx response into a
`CliError` and drops the body. That is right for an API where a
rejection is a failure, and wrong for one where it is an answer: the
OAuth device grant replies `400 {"error": "authorization_pending"}` to
every poll until its owner approves in a browser, so a client that could
only see "the request failed" could never complete the flow. `GET
/v1/user` has the same shape from the other side -- a refused credential
(403) and a deployment too old to serve the route (404) are different
sentences to the user, and both differ from a network failure.

`post_form` and `get_outcome` return an `ApiOutcome`: the decoded body
on success, and the status plus the decoded error body on a terminal
rejection. Retries are untouched -- 5xx and rate limits are retried
before anything is handed back -- and a rejection whose body does not
deserialize as the protocol's error type stays a plain `CliError`, so a
proxy's HTML 502 page is as diagnosable as it is on every other verb.

Inside the driver, `tolerate_not_found: bool` becomes a three-valued
`OnTerminalError`, because the third case is not a boolean and a third
bool would have tripped `clippy::fn_params_excessive_bools`. The one
ordering change it forces: the retry decision is now taken before the
error body is read, since rendering the message consumes the very bytes
the caller asked for.

`post_form_no_response` covers the revocation endpoint, which answers
200 with an empty body. Form encoding needs reqwest's `form` feature;
the device grant is the only thing in the CLI that sends one.

Fixes MRGFY-8703

Change-Id: I9339bdfbb74edbf3399a529e602007d96bfda074
@sileht
sileht force-pushed the devs/sileht/mrgfy-8703-cli-auth-commands/let-caller-read-endpoint-s-rejection-body--9339bdfb branch from 3c5d1dd to 9ca0419 Compare September 4, 2026 23:40
@sileht
sileht force-pushed the devs/sileht/mrgfy-8703-cli-auth-commands/store-mergify-credential-os-keychain--bd91cd6d branch from 7273861 to e0609e3 Compare September 4, 2026 23:40
@sileht

sileht commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

Revision history

# Type Changes Reason Date
1 initial 3c5d1dd 2026-09-04 23:40 UTC
2 rebase 3c5d1dd → 9ca0419 (rebase only) 2026-09-04 23:40 UTC

@mergify
mergify Bot had a problem deploying to Mergify Merge Protections September 4, 2026 23:41 Failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant