Error handling
This page is the decision guide behind the error codes table. The short rule: an error is never a pass.
Three kinds of failure
Section titled “Three kinds of failure”| Kind | Where | Example | What to do |
|---|---|---|---|
| A verdict | siteverify HTTP 200 with success: false |
timeout-or-duplicate, invalid-input-response |
Reject the request; ask for a new token. |
| An API error | non-200 with { "error": { "code", "message", "requestId" } } |
rate-limited (429), unauthorized (401) |
Branch on code, never on message. |
| Your own failure | no response | timeout, DNS, TLS | Decide fail open or closed; the token may be spent. |
Retry rules
Section titled “Retry rules”429 rate-limited: waitRetry-After(1 second), retry once.500and503: retry once after a short pause. Do not retry a spent token: after a network error or HTTP error it may be used up, so ask the client for a new one.400,401,402,403,404,410,413,415: do not retry. Fix the cause.
Fail open or fail closed
Section titled “Fail open or fail closed”| Flow | Suggested |
|---|---|
| Newsletter, comment, search | Fail open, and log it. |
| Signup, contact form | Fail open with a slower path (email confirmation), or closed. |
| Login, password reset | Fail closed or step up. |
| Payment, SMS send, account change | Fail closed. |
For password check, a 503 means “could not check”, never “not leaked”.
What to log
Section titled “What to log”Log the SDK reason or code, the HTTP status, the requestId, the score and the reasons. Never log the token, the secret key or a password. See scores and reasons.
In the SDKs
Section titled “In the SDKs”Node and Python verify never throw: gate on the outcome. Other methods throw an SDK error with code, status, requestId and retryAfterSeconds for a 429. Each SDK page lists its names: Node, Python, PHP, Go, Java.