Error Handling
Babel Shield throws typed errors with properties that help you handle each failure mode correctly.
Error Types
| Error | Status | Retryable | Key Properties |
|---|---|---|---|
ValidationError |
400/422 | No | code, errors.field |
RateLimitError |
429 | Yes | retryAfter, rateLimitCode, clientSide |
NetworkError |
-- | Yes | possibleCors |
TimeoutError |
-- | Yes | timeout (ms) |
AuthError |
401/402/403 | No | code, status |
ServerError |
5xx | Yes | status |
Every error has a retryable property. Only retry when retryable is true.
Handling Errors
Use error.name to identify the type and access its specific properties:
try {
const result = await BabelShield.moderate(content);
}
catch (error) {
switch (error.name) {
case 'ValidationError':
// Payload is invalid -- fix before retrying
console.log('Field:', error.errors?.field);
break;
case 'RateLimitError':
// Too many requests -- wait before retrying
console.log('Retry after:', error.retryAfter, 'seconds');
console.log('API error code:', error.rateLimitCode);
console.log('Client-side limit:', error.clientSide);
break;
case 'NetworkError':
// Network failure -- check connectivity
if (error.possibleCors) {
console.log('Possible CORS issue -- check domain authorization');
}
break;
case 'TimeoutError':
// Request timed out
console.log('Timed out after:', error.timeout, 'ms');
break;
case 'AuthError':
// Authentication failed -- check token
console.log('Auth error:', error.code);
// Codes: 'invalid_token', 'subscription_required', 'forbidden'
break;
case 'ServerError':
// Server-side error -- retry
console.log('Server error:', error.status);
break;
}
}
Retry with Exponential Backoff
For retryable errors, use exponential backoff with jitter to avoid thundering herd problems:
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
}
catch (error) {
if (!error.retryable || attempt === maxRetries) {
throw error;
}
const delay = baseDelay * Math.pow(2, attempt - 1);
const jitter = Math.random() * 200;
const wait = Math.min(delay + jitter, 10000);
console.log(`Retry ${attempt}/${maxRetries} in ${Math.round(wait)}ms`);
await new Promise(resolve => setTimeout(resolve, wait));
}
}
}
// Usage
try {
const result = await retryWithBackoff(() =>
BabelShield.moderate(content)
);
}
catch (error) {
console.error('All retries failed:', error.name, error.message);
}
The built-in API client already retries automatically on timeout, 429, 500, and 503 status codes (up to 2 retries). Use
the retryWithBackoff pattern for additional retry control in your application layer.
Rate-Limit Hints (retryAfter and rateLimitCode)
On a 429, RateLimitError surfaces two hints from the API (matching the PHP library):
retryAfter-- seconds to wait before retrying, ornullwhen the server sent no usable hint. The value is resolved in this order:Retry-Afterheader, thenX-RateLimit-Resetheader, then the response body'serrors.retry_after. All sources are interpreted as relative seconds (never a timestamp), and the surfaced value is capped at 86,400 seconds (24 hours). Unparseable or negative values are ignored.rateLimitCode-- the API's machine-readable 429error_code(for exampleRATE_LIMIT_EXCEEDEDon/moderate, orTHROTTLE_LIMIT_EXCEEDEDon the auth/token endpoints), ornull. Treat it as an opaque string for logging and branching; it is length-capped at 64 characters and distinct fromerror.code, which is always'rate_limit'.
Do not blindly sleep() on the hint. retryAfter can legitimately be hours long (up to the 24-hour cap) when an
account-level quota is exhausted -- pausing a browser session that long is never useful. Treat large values as "stop
retrying and degrade gracefully" rather than as a literal wait. The built-in automatic retry already does this: its
per-attempt delay is clamped to at most 10 seconds regardless of retryAfter.
Fail-Open Behavior
By default, failOpen is true. If moderation fails for any reason, forms submit normally so users are never blocked
by a service outage.
const result = await BabelShield.init({
apiToken: 'YOUR_API_TOKEN',
failOpen: true
});
if (result.failOpen) {
// Initialization failed, but forms still work
console.warn('Moderation unavailable:', result.message);
}
Set failOpen: false to block form submissions when moderation is unavailable. Use this for high-risk forms where
unmoderated content is not acceptable:
BabelShield.init({
apiToken: 'YOUR_API_TOKEN',
failOpen: false
});
Event-Based Error Monitoring
Use the event system for centralized error logging without wrapping every call in try-catch:
// Monitor all API errors
BabelShield.on('api:error', ({ error }) => {
console.error('API Error:', error.name, error.message);
// Send to your error tracking service
errorTracker.capture(error);
});
// Monitor all API responses
BabelShield.on('api:response', (result) => {
console.log('Response received:', result.cached ? '(cached)' : '(fresh)');
});
Events fire regardless of whether the caller handles the error. This is useful for metrics, alerting, and centralized logging alongside per-call error handling.
Next Steps
- ESM Import & Programmatic Use -- Cache stats, rate limit monitoring, and manual form control
- Troubleshooting -- Common errors, diagnostics, and debug mode
- API Reference -- Full error code reference