Error Handling
Handle moderation errors gracefully in production.
Error Types
Vettly returns specific HTTP status codes for different error conditions:
| Status | Error | Cause |
|---|---|---|
| 401 | Unauthorized | Invalid or missing API key |
| 402 | Payment Required | Quota exceeded |
| 422 | Unprocessable Entity | Invalid request parameters |
| 429 | Too Many Requests | Rate limit exceeded |
| 5xx | Server Error | Internal error (retry automatically) |
SDK Error Handling
Both TypeScript and Python SDKs provide typed exceptions:
typescript
import {
ModerationClient,
VettlyAuthError,
VettlyRateLimitError,
VettlyQuotaError,
VettlyValidationError,
VettlyError,
} from '@nextauralabs/vettly-sdk'
const client = new ModerationClient({ apiKey: 'sk_live_...' })
try {
const result = await client.check({
content: 'User message',
policyId: 'moderate',
contentType: 'text'
})
} catch (error) {
if (error instanceof VettlyAuthError) {
// 401: Invalid API key
console.error('Check your API key')
} else if (error instanceof VettlyRateLimitError) {
// 429: Rate limited
console.error(`Retry after ${error.retryAfter}s`)
} else if (error instanceof VettlyQuotaError) {
// 402: Quota exceeded
console.error(`Upgrade plan: ${error.quota}`)
} else if (error instanceof VettlyValidationError) {
// 422: Invalid request
console.error(`Fix request: ${error.errors}`)
} else if (error instanceof VettlyError) {
// 5xx: Server error (SDK auto-retries these)
console.error('Server error, try again later')
}
}python
from vettly import (
ModerationClient,
VettlyAuthError,
VettlyRateLimitError,
VettlyQuotaError,
VettlyValidationError,
VettlyServerError,
)
client = ModerationClient(api_key="sk_live_...")
try:
result = client.check(content="User message", policy_id="moderate")
except VettlyAuthError:
# 401: Invalid API key
print("Check your API key")
except VettlyRateLimitError as e:
# 429: Rate limited
print(f"Retry after {e.retry_after}s")
except VettlyQuotaError as e:
# 402: Quota exceeded
print(f"Upgrade plan: {e.quota}")
except VettlyValidationError as e:
# 422: Invalid request
print(f"Fix request: {e.errors}")
except VettlyServerError:
# 5xx: Server error (SDK auto-retries these)
print("Server error, try again later")Automatic Retry
Both SDKs automatically retry failed requests with exponential backoff:
- Retried: Rate limits (429) and server errors (5xx)
- Not retried: Auth errors (401), validation errors (422), quota errors (402)
- Backoff: 1s, 2s, 4s, etc. (configurable)
- Max retries: 3 by default (configurable)
- Retry-After: Respects the
Retry-Afterheader from the API
Configuration
typescript
const client = new ModerationClient({
apiKey: 'sk_live_...',
maxRetries: 5, // Default: 3
retryDelay: 2000, // Default: 1000ms
timeout: 60000, // Default: 30000ms
})python
client = ModerationClient(
api_key="sk_live_...",
max_retries=5, # Default: 3
retry_delay=2.0, # Default: 1.0s
timeout=60.0, # Default: 30.0s
)Fail Open vs Fail Closed
Choose a strategy for when moderation fails:
Fail Open (Allow on Error)
Allow content through when moderation is unavailable. Use when:
- Uptime is more important than safety
- You have other safeguards (human review queue)
- Content is low-risk (internal tools)
typescript
async function moderateContent(content: string): Promise<boolean> {
try {
const result = await client.check({
content,
policyId: 'moderate',
contentType: 'text'
})
return result.action !== 'block'
} catch (error) {
// Fail open: allow on error
console.error('Moderation unavailable:', error)
return true
}
}python
def moderate_content(content: str) -> bool:
try:
result = client.check(content=content, policy_id="moderate")
return result.action != "block"
except Exception as e:
# Fail open: allow on error
print(f"Moderation unavailable: {e}")
return TrueFail Closed (Block on Error)
Block content when moderation is unavailable. Use when:
- Safety is more important than uptime
- Content is high-risk (public-facing, children's app)
- You can afford to reject valid content temporarily
typescript
async function moderateContent(content: string): Promise<boolean> {
try {
const result = await client.check({
content,
policyId: 'moderate',
contentType: 'text'
})
return result.action !== 'block'
} catch (error) {
// Fail closed: block on error
console.error('Moderation unavailable:', error)
return false
}
}python
def moderate_content(content: str) -> bool:
try:
result = client.check(content=content, policy_id="moderate")
return result.action != "block"
except Exception as e:
# Fail closed: block on error
print(f"Moderation unavailable: {e}")
return FalseHybrid: Queue for Review
Queue content for human review when moderation fails:
typescript
async function moderateContent(content: string, userId: string) {
try {
const result = await client.check({
content,
policyId: 'moderate',
contentType: 'text'
})
if (result.action === 'block') {
return { allowed: false, reason: 'blocked' }
}
return { allowed: true }
} catch (error) {
// Queue for human review
await reviewQueue.add({
content,
userId,
error: error.message,
createdAt: new Date()
})
// Allow through but flag for review
return { allowed: true, pendingReview: true }
}
}Logging and Monitoring
Log moderation decisions for debugging and analytics:
typescript
const result = await client.check({
content,
policyId: 'moderate',
contentType: 'text'
})
// Log decision
console.log({
decisionId: result.decisionId,
action: result.action,
safe: result.safe,
latency: result.latency,
categories: result.categories.filter(c => c.triggered)
})Track error rates in your monitoring:
typescript
try {
await client.check({ ... })
metrics.increment('moderation.success')
} catch (error) {
metrics.increment('moderation.error', { type: error.constructor.name })
throw error
}Circuit Breaker Pattern
For high-traffic applications, consider a circuit breaker:
typescript
import CircuitBreaker from 'opossum'
const breaker = new CircuitBreaker(
(content: string) => client.check({
content,
policyId: 'moderate',
contentType: 'text'
}),
{
timeout: 5000, // 5s timeout
errorThresholdPercentage: 50,
resetTimeout: 30000 // Try again after 30s
}
)
breaker.on('open', () => {
console.warn('Moderation circuit opened - falling back')
})
// Use the breaker
const result = await breaker.fire(content)Next Steps
- Troubleshooting - Common issues and solutions
- Error Codes Reference - Complete error code list
- Best Practices - Production deployment tips