Error Handling
All Aerostack SDK methods throw typed errors on failure. This guide covers the error types, how to catch and handle them, and strategies for retrying failed operations.
Error types
Section titled “Error types”The SDK throws specific error classes depending on what went wrong:
| Error Class | When it occurs | HTTP Status |
|---|---|---|
ClientError | Invalid request, missing parameters, bad input | 400 |
AuthenticationError | Invalid or expired token, unauthorized access | 401 |
ForbiddenError | Valid token but insufficient permissions | 403 |
NotFoundError | Resource does not exist | 404 |
ValidationError | Input fails server-side validation rules | 422 |
RateLimitError | Too many requests | 429 |
ServerError | Unexpected server failure | 500+ |
NetworkError | Connection failed, timeout, DNS failure | N/A |
Error properties
Section titled “Error properties”Every SDK error includes:
interface AerostackError { message: string // Human-readable error description code: string // Machine-readable error code (e.g., 'AUTH_INVALID_TOKEN') status: number // HTTP status code details?: any // Additional context (validation errors, rate limit info)}Basic try/catch
Section titled “Basic try/catch”import { useAuth } from '@aerostack/react'
function LoginForm() { const { signIn, error, loading } = useAuth()
const handleSubmit = async (email: string, password: string) => { try { await signIn(email, password) // Success -- user and tokens are set } catch (err) { // err.message is also reflected in the `error` state if (err.code === 'AUTH_INVALID_CREDENTIALS') { // Wrong email or password } else if (err.code === 'AUTH_ACCOUNT_LOCKED') { // Too many failed attempts } else if (err.code === 'AUTH_EMAIL_NOT_VERIFIED') { // Redirect to verification page } } }
return ( <div> {/* The error state is set automatically by useAuth */} {error && <p style={{ color: 'red' }}>{error}</p>} </div> )}import { AerostackClient, AuthenticationError, ValidationError, RateLimitError } from '@aerostack/sdk'
const { sdk } = new AerostackClient({ projectId, apiKey })
try { const { results } = await sdk.db.query('SELECT * FROM users WHERE id = ?', [userId])} catch (err) { if (err instanceof AuthenticationError) { // Token expired or invalid -- refresh and retry } else if (err instanceof ValidationError) { // Bad input -- check err.details for field-level errors console.error('Validation failed:', err.details) } else if (err instanceof RateLimitError) { // Too many requests -- wait and retry const retryAfter = err.details?.retryAfter ?? 60 console.error(`Rate limited. Retry after ${retryAfter}s`) } else { // Unknown error console.error('Unexpected error:', err.message) }}Common error codes
Section titled “Common error codes”Authentication errors
Section titled “Authentication errors”| Code | Description |
|---|---|
AUTH_INVALID_CREDENTIALS | Wrong email or password |
AUTH_INVALID_TOKEN | Access token is malformed or expired |
AUTH_ACCOUNT_LOCKED | Account locked due to too many failed attempts (60-minute lockout) |
AUTH_EMAIL_NOT_VERIFIED | Email verification required before sign-in |
AUTH_USER_NOT_FOUND | No account with this email |
AUTH_EMAIL_ALREADY_EXISTS | Email already registered |
AUTH_OTP_EXPIRED | OTP code has expired (10-minute window) |
AUTH_OTP_INVALID | Wrong OTP code |
AUTH_REFRESH_TOKEN_INVALID | Refresh token expired or revoked |
Database errors
Section titled “Database errors”| Code | Description |
|---|---|
DB_QUERY_ERROR | SQL syntax error or constraint violation |
DB_CONSTRAINT_VIOLATION | Unique constraint, foreign key, or NOT NULL violation |
DB_TABLE_NOT_FOUND | Referenced table does not exist |
Storage errors
Section titled “Storage errors”| Code | Description |
|---|---|
STORAGE_FILE_TOO_LARGE | Upload exceeds size limit |
STORAGE_FILE_NOT_FOUND | Requested file does not exist |
STORAGE_QUOTA_EXCEEDED | Storage quota for the project is full |
Rate limit errors
Section titled “Rate limit errors”| Code | Description |
|---|---|
RATE_LIMIT_EXCEEDED | Too many requests in the time window |
AUTH_OTP_VERIFY_LIMIT | Too many OTP verification attempts (max 3) |
Retry strategies
Section titled “Retry strategies”Simple retry with backoff
Section titled “Simple retry with backoff”async function withRetry<T>( fn: () => Promise<T>, maxRetries = 3, baseDelay = 1000): Promise<T> { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn() } catch (err) { // Don't retry client errors (bad input won't fix itself) if (err.status >= 400 && err.status < 500 && err.status !== 429) { throw err }
if (attempt === maxRetries) throw err
// Exponential backoff: 1s, 2s, 4s const delay = baseDelay * Math.pow(2, attempt) await new Promise(resolve => setTimeout(resolve, delay)) } } throw new Error('Unreachable')}
// Usageconst users = await withRetry(() => sdk.db.query('SELECT * FROM users WHERE active = 1'))Retry with rate limit awareness
Section titled “Retry with rate limit awareness”async function withRateLimitRetry<T>(fn: () => Promise<T>): Promise<T> { try { return await fn() } catch (err) { if (err.code === 'RATE_LIMIT_EXCEEDED' && err.details?.retryAfter) { // Wait the exact time the server tells us await new Promise(resolve => setTimeout(resolve, err.details.retryAfter * 1000) ) return await fn() } throw err }}Token refresh on auth error
Section titled “Token refresh on auth error”The SDK handles token refresh automatically on 401 responses. For manual handling:
async function authenticatedRequest<T>(fn: () => Promise<T>): Promise<T> { try { return await fn() } catch (err) { if (err.code === 'AUTH_INVALID_TOKEN') { // Token expired -- refresh and retry await refreshAccessToken(tokens.refreshToken) return await fn() } throw err }}React error boundaries
Section titled “React error boundaries”For unexpected errors, wrap SDK-dependent components in an error boundary:
interface Props { children: ReactNode fallback: ReactNode}
interface State { hasError: boolean error: Error | null}
export class SDKErrorBoundary extends Component<Props, State> { state: State = { hasError: false, error: null }
static getDerivedStateFromError(error: Error) { return { hasError: true, error } }
render() { if (this.state.hasError) { return this.props.fallback } return this.props.children }}
// Usage<SDKErrorBoundary fallback={<p>Something went wrong. Please reload.</p>}> {/* Your SDK-dependent components go here */}</SDKErrorBoundary>Debugging tips
Section titled “Debugging tips”-
Check the
errorstate — React hooks (useAuth,useDb) set anerrorstring automatically. Display it in your UI during development. -
Log error details — The
detailsproperty often contains field-level validation errors or rate limit retry-after values. -
Check the network tab — SDK errors include the HTTP status code and response body. The browser network tab shows the exact request and response.
-
Enable verbose logging — Set the
debugoption when initializing the SDK:
const client = new AerostackClient({ projectId, apiKey, debug: true, // Logs all requests and responses to console})- Check the dashboard — Error logs are available in the Aerostack Dashboard under your project’s Logs section.