Errors and denied access
You reach the photo service's approval screen and decide not to connect the printer. That is a valid outcome. The printer should let you return to your photo book without pretending the connection succeeded or repeatedly sending you back for approval.
Other attempts fail because of configuration mistakes, expired codes, or lost network responses. Handling them well starts with identifying where the exchange stopped.
A token request is rejected
An error at the token endpoint comes back directly to the client. For example:
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"error": "invalid_grant"
}
This can cover an expired or previously used code, a code binding mismatch, or a failed PKCE verification. It does not identify one unique cause. invalid_client instead concerns failed client authentication, while invalid_request indicates a malformed request. The client should handle the documented error and HTTP behavior, not assume all failures have the same status or recovery path.
The person using the printer needs a clear next step. The developer needs enough safe diagnostic context to investigate. Record the stage, a local correlation ID, and a safe error category. Do not copy full callback URLs, authorization headers, codes, verifiers, or tokens into logs.
Optional error descriptions are untrusted text. Do not insert them into HTML as markup, and do not automatically follow supplied error links.
When the outcome is uncertain
Suppose the printer sends its token request, but the connection drops before it receives a response. The authorization server might have rejected the request, or it might have consumed the code and issued a token whose response was lost.
A timeout therefore means the client did not obtain a confirmed result. It does not prove that the server did nothing.
Sending the same code again is not a reliable way to recover. A code presented twice may have been stolen, so the authorization server must reject the second attempt and should, where it can, revoke any tokens already issued from that code. If the first request did reach the server, the retry is refused with invalid_grant, the same answer a failed first request would have produced, and it may cause the server to revoke the token whose response was lost.
For this teaching flow, the printer reports that it could not finish the connection and offers a fresh attempt. It does not silently remove PKCE, change the return address, or keep prompting for consent in a loop.
Even a connection that succeeds leaves the printer with one ten-minute access token. When it expires, the printer has no way to get another without sending you through the flow again, which is the gap The refresh token grant fills.