Exchange Code Attempt Limit Exceeded
exchange_code_attempt_limit_exceeded429
Too many failed attempts to exchange the verification code. Wait 1 hour before retrying.
What this means
The exchange code used in OAuth-style flows has been submitted too many times with wrong or invalid values from this IP, and we've locked further attempts for an hour. The retry_after field is 3600 by contract. This is a brute-force defense: it stops an attacker from guessing short-lived codes by exhaustive trial.
When you'll see this
- A user repeatedly typed the wrong verification code (mistyping the digits or pasting the wrong code).
- A bug in your client code resubmitted the same code multiple times without realizing it had already been consumed.
- An attacker tried to guess valid exchange codes by submitting random values.
- A test or QA flow is exercising the exchange endpoint in a tight loop with invalid codes.
Learn more about how this works
Exchange codes are short-lived single-use tokens issued during authentication flows. They're checked against our server-side store; an invalid submission counts against an IP-scoped attempt budget. Hit the ceiling and we lock further attempts from that IP for an hour, even if the next attempt would have been correct.
In practice: a real user rarely hits this. Most production occurrences are bug-driven — client code that retries an already-consumed code, or a UI that resubmits on Enter without checking what was submitted. The fix is almost always on the client side, not the user's.
Example response
{
"success": false,
"error": "exchange_code_attempt_limit_exceeded",
"message": "Too many failed exchange attempts.",
"details": [],
"retry_after": 3600,
"doc_url": "https://docs.asterwise.com/reference/errors/exchange_code_attempt_limit_exceeded",
"request_id": "req_01HXYZABCDEFGH",
"timestamp": "2026-05-25T12:34:56Z"
}
- Wait 60 minutes. The attempt counter for this IP will reset.
- While waiting, check why so many attempts happened. If a user got stuck typing the wrong code, address the input UX. If your client retried automatically, fix the retry logic.
- If the user can still authenticate through a different mechanism (a fresh magic link from a different IP), use that path.
Don't auto-retry on this error. The whole point of the limit is that retrying is what triggered it.
Python:
Production handler
- Python
- TypeScript
import httpx
def exchange_code(code, base_url, headers):
response = httpx.post(
f"{base_url}/v1/auth/exchange",
headers=headers,
json={"code": code},
timeout=10,
)
if response.status_code == 429:
body = response.json()
if body.get("error") == "exchange_code_attempt_limit_exceeded":
wait_minutes = (body.get("retry_after", 3600)) // 60
return {
"ok": False,
"user_message": (
f"Too many failed verification attempts. "
f"Please wait {wait_minutes} minutes and request "
"a fresh sign-in link."
),
}
response.raise_for_status()
return {"ok": True, "data": response.json()}
async function exchangeCode(code: string, baseUrl: string, headers: HeadersInit) {
const response = await fetch(`${baseUrl}/v1/auth/exchange`, {
method: "POST",
headers,
body: JSON.stringify({ code }),
});
if (response.status === 429) {
const body = await response.json();
if (body.error === "exchange_code_attempt_limit_exceeded") {
const waitMinutes = Math.floor((body.retry_after ?? 3600) / 60);
return {
ok: false,
userMessage:
`Too many failed verification attempts. ` +
`Please wait ${waitMinutes} minutes and request ` +
`a fresh sign-in link.`,
};
}
}
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return { ok: true, data: await response.json() };
}
Avoid this error by
- Validate code format on the client (length, charset) before submission. Catching obvious garbage in the UI prevents wasted attempts against the server.
- Never auto-resubmit the same exchange code. Once a submission returns an error, require the user to take an explicit action before the next attempt.
- After 3 consecutive failed attempts in your UI, suggest the user request a fresh magic link instead of trying more codes.
- Don't store exchange codes anywhere durable. They're single-use, short-lived, and should be discarded immediately after submission attempts.