Error Handling in SDKs
Every SDK call that reaches the Descope servers can fail. When it does, the SDK reports a Descope error code (for example E061102), a human-readable description, and sometimes a more specific message. You can look up any code on the Common Errors page.
Note
For a list of common error codes and what they mean, see the Common Errors reference.
Checking for Errors by SDK
Each tab shows how one SDK types its errors and which codes it exposes, plus how to:
- Check for a specific error code
- Check for a category, such as unauthorized or rate limited
- Read the code and description off an error
The Node.js SDK does not throw when the server returns an error. Every call returns an SdkResponse<T> — check ok before using data, and read the details off error. The one exception is a response whose body is not valid JSON, which rejects instead of returning.
{
ok: boolean; // false when the request failed
code?: number; // HTTP status code (e.g. 401, 429)
error?: {
errorCode: string; // Descope error code, e.g. "E061102"
errorDescription: string; // human-readable summary
errorMessage?: string; // extra context (not always present)
};
data?: T; // present when ok === true
}Import the client and the typed error-code map (DescopeErrors):
import DescopeClient from '@descope/node-sdk';
const descopeClient = DescopeClient({ projectId: '__ProjectID__' });
const { DescopeErrors } = DescopeClient;
const resp = await descopeClient.otp.verify.email(loginId, code);
if (!resp.ok) {
// Read the code + description off the response
console.log(resp.error?.errorCode); // "E061103"
console.log(resp.error?.errorDescription);
console.log(resp.code); // HTTP status, e.g. 401
// Check for a specific typed error code
if (resp.error?.errorCode === DescopeErrors.tooManyOTPAttempts) {
// too many wrong OTP attempts (E061103)
}
// Check for a category using the HTTP status code
if (resp.code === 429) {
// rate limited
}
} else {
// Success — use resp.data
const { sessionJwt } = resp.data;
}Codes exposed on DescopeErrors:
| Constant | Code |
|---|---|
DescopeErrors.badRequest | E011001 |
DescopeErrors.missingArguments | E011002 |
DescopeErrors.invalidRequest | E011003 |
DescopeErrors.invalidArguments | E011004 |
DescopeErrors.wrongOTPCode | E061102 |
DescopeErrors.tooManyOTPAttempts | E061103 |
DescopeErrors.enchantedLinkPending | E062503 |
DescopeErrors.userNotFound | E062108 |
When the Error Body Cannot Be Parsed
A failing response does not always contain a Descope error body — a gateway may return an HTML error page, or an empty 401. Node.js is the one SDK that does not produce an SdkResponse in that case: parsing the body is what populates error, so a non-JSON body makes the call reject instead of returning. Wrap calls in try/catch in addition to checking ok if you need to survive a malformed gateway response.
Automatic Retries
Before surfacing an error, the SDKs transparently retry requests that failed with a transient server status.
The NodeJS, Python, Go, Java, PHP, and .NET SDKs all retry HTTP 503, 520, 521, 522, 524, and 530 — the Cloudflare and service-unavailable statuses — up to three times after the initial attempt, waiting 100ms before the first retry and 5 seconds before each of the next two.
The Ruby SDK is the exception: it does not retry those statuses at all. Instead it retries only HTTP 429, three times by default, using exponential backoff with jitter.