Error Handling
Learn how to handle errors gracefully in Magical Auth.
Error Response Format
All errors follow a consistent format:
{
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"status": 400,
"request_id": "req_abc123",
"details": {
"fields": {
"phone_number": "Must be a valid E.164 phone number format"
}
}
}
Error Codes
Client Errors (4xx)
| Error Code | Status | Description |
|---|---|---|
BAD_REQUEST | 400 | Invalid JSON in request body |
VALIDATION_ERROR | 400 | Request validation failed (see details.fields) |
MISSING_PARAMETERS | 400 | Required parameters missing |
SESSION_NOT_FOUND | 404 | Session expired or not found |
CARRIER_NOT_ELIGIBLE | 422 | Carrier doesn't support Magical Auth |
UNSUPPORTED_PLATFORM | 422 | Browser/platform not supported for the selected strategy |
INVALID_CREDENTIAL_FORMAT | 422 | Invalid credential structure |
INVALID_VERIFICATION | 422 | Authentication response invalid |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
Server Errors (5xx)
| Error Code | Status | Description |
|---|---|---|
SERVICE_UNAVAILABLE | 503 | Carrier service temporarily unavailable |
INTERNAL_SERVER_ERROR | 500 | Unexpected server error |
Desktop Flow Responses
For desktop (QR code) sessions, the /process endpoint (verify-phone-number or get-phone-number) blocks server-side while waiting for the mobile device to complete authentication. The following non-error responses require specific handling:
| Status | Meaning | Body | SDK Action |
|---|---|---|---|
| 200 | Mobile completed authentication | Normal verified response | Done — use the result |
| 202 Accepted | Timeout — session still active | { "status": "waiting", "retry": true, "session_expires_in_seconds": N } | Re-issue the same /process request |
| 503 Service Unavailable | Rolling deploy — server shutting down | Retry-After: 1 header | Retry after the Retry-After interval (typically 1s) |
Handling the 202 retry loop:
async function desktopVerify(sessionKey, credential, session) {
while (true) {
const response = await fetch('/api/phone-auth/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session, credential, use_case: 'VerifyPhoneNumber' }),
signal: AbortSignal.timeout(60_000), // 60s client timeout
});
if (response.status === 200) {
return await response.json(); // verified result
}
if (response.status === 202) {
const body = await response.json();
if (!body.retry) throw new Error('Session expired');
continue; // re-issue the same request
}
if (response.status === 503) {
const retryAfter = response.headers.get('Retry-After') || '1';
await new Promise(r => setTimeout(r, parseInt(retryAfter) * 1000));
continue;
}
throw new Error(`Unexpected status: ${response.status}`);
}
}
tip
The SDK handles 202/503 retry logic automatically. This pattern is only needed if you are calling the Glide API directly without the SDK.
Handling Errors
JavaScript (Web Client SDK)
import { ERROR_CODES } from '@glideidentity/glide-fe-sdk-web';
try {
const result = await client.authenticate({
use_case: USE_CASE.VERIFY_PHONE_NUMBER,
phone_number: '+14155551234'
});
} catch (error) {
switch (error.code) {
case ERROR_CODES.CARRIER_NOT_ELIGIBLE:
// Show SMS fallback
showSMSVerification();
break;
case ERROR_CODES.UNSUPPORTED_PLATFORM:
// Show browser requirement message
showBrowserRequirement();
break;
case ERROR_CODES.USER_CANCELLED:
// User closed the prompt
break;
default:
console.error('Error:', error.code, error.message);
}
}
Node.js (Backend SDK)
import { MagicalAuthError } from '@glideidentity/glide-be-node-magical-auth';
try {
const response = await client.prepare(request);
} catch (error) {
if (error instanceof MagicalAuthError) {
console.log('Code:', error.code);
console.log('Message:', error.message);
console.log('Status:', error.status);
console.log('Request ID:', error.requestId);
}
}
Go (Backend SDK)
response, err := glideClient.MagicalAuth.Prepare(ctx, &req)
if err != nil {
if glideErr, ok := err.(*glide.MagicalAuthError); ok {
log.Printf("Code: %s, Message: %s, Status: %d",
glideErr.Code, glideErr.Message, glideErr.Status)
}
}
Java (Backend SDK)
import com.glideidentity.exception.MagicalAuthError;
try {
PrepareResponse response = glideClient.magicalAuth.prepare(request);
} catch (MagicalAuthError e) {
log.info("Code: {}, Message: {}, Status: {}",
e.getCode(), e.getMessage(), e.getStatus());
if (e.isRetryable()) {
// Implement retry with backoff for 429, 503, 5xx errors
}
}
Phone Number Mismatch
When verifying a phone number, a mismatch is not an error. Instead, you'll receive a successful response with verified: false:
{
"phone_number": "+14155551234",
"verified": false
}
This allows you to handle mismatches gracefully in your UI.
Best Practices
- Always provide user-friendly messages - Don't expose raw error codes to users
- Implement fallbacks - Have SMS/email backup for unsupported carriers
- Log request IDs - Include
request_idin your logs for debugging - Handle rate limits - Implement exponential backoff for retries
- Check platform support - Show browser requirements before starting the flow