Error Handling
This guide explains how the SynthesQ API handles and reports errors, including error response formats, validation errors, and best practices for error handling in your application.
Error Response Format
All error responses follow a consistent JSON structure to make error handling predictable.
Standard Error Response
{
"error": "Error type or title",
"message": "Human-readable error description",
"errors": {
"field_name": [
"Specific error message"
]
}
}HTTP Status Codes
The API uses standard HTTP status codes to indicate the type of error:
Client Errors (4xx)
400 Bad Request
The request was malformed or contains invalid syntax.
Example:
{
"error": "Bad Request",
"message": "The request could not be understood by the server"
}Common causes:
- Invalid JSON syntax
- Missing required headers
- Malformed URL parameters
401 Unauthorized
Authentication is required or has failed.
Example:
{
"error": "Unauthorized",
"message": "Unauthenticated."
}Common causes:
- Missing
Authorizationheader - Invalid or expired access token
- Revoked authentication credentials
Solution:
# Include valid Bearer token
curl https://api.synthesq.com/api/v1/operations/products \
-H "Authorization: Bearer YOUR_VALID_TOKEN"403 Forbidden
The request is valid, but you don't have permission to perform this action.
Example:
{
"error": "Forbidden",
"message": "You do not have permission to perform this action"
}Common causes:
- Insufficient user permissions/roles
- Trying to access another tenant's data
- Action not allowed for your account type
404 Not Found
The requested resource doesn't exist.
Example:
{
"error": "Not Found",
"message": "Resource not found"
}Common causes:
- Invalid resource ID
- Resource was deleted
- Incorrect endpoint URL
Example:
# Invalid product ID
GET /api/v1/operations/products/99999
# Response
{
"error": "Not Found",
"message": "Product not found"
}422 Unprocessable Entity
The request was well-formed but contains validation errors.
Example:
{
"errors": {
"name": [
"Product name is required"
],
"sku": [
"SKU already exists"
],
"selling_price": [
"Selling price must be a valid number",
"Selling price cannot be negative"
],
"cost_price": [
"Cost price should not be higher than selling price (negative margin)"
],
"reserved_quantity": [
"Reserved quantity cannot exceed stock quantity"
]
}
}Common validation errors:
| Field Error | Description |
|---|---|
required | Field is required but missing |
unique | Value must be unique (e.g., SKU, email) |
exists | Referenced resource doesn't exist (e.g., category_id) |
min / max | Value outside allowed range |
numeric | Value must be a number |
email | Invalid email format |
url | Invalid URL format |
429 Too Many Requests
You've exceeded the rate limit.
Example:
{
"message": "Too many requests. Please try again later.",
"retry_after": 45
}Response headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1642598400
Retry-After: 45Solution: Wait for the time specified in retry_after (seconds) or Retry-After header before making another request.
Server Errors (5xx)
500 Internal Server Error
An unexpected error occurred on the server.
Example:
{
"error": "Failed to create product",
"message": "An unexpected error occurred. Please try again later."
}In development mode (app.debug=true), additional details may be included:
{
"error": "Failed to create product",
"message": "SQLSTATE[23000]: Integrity constraint violation",
"errors": {
"exception": "PDOException: SQLSTATE[23000]...",
"trace": "Stack trace..."
}
}Production Note
Detailed error traces are only shown in development environments. Production responses will hide sensitive implementation details.
503 Service Unavailable
The service is temporarily unavailable (maintenance, overload, etc.).
Example:
{
"error": "Service Unavailable",
"message": "The service is temporarily unavailable. Please try again later."
}Validation Errors in Detail
Field-Level Validation
Validation errors are returned with the specific field that failed validation:
Request:
POST /api/v1/operations/products
{
"name": "",
"sku": "EXISTING-SKU",
"selling_price": -100
}Response (422):
{
"errors": {
"name": [
"Product name is required"
],
"sku": [
"SKU already exists"
],
"selling_price": [
"Selling price cannot be negative"
]
}
}Business Rule Validation
Some errors represent business rule violations:
Request:
{
"name": "Test Product",
"sku": "TEST-001",
"selling_price": 100.00,
"cost_price": 150.00,
"stock_quantity": 50,
"reserved_quantity": 75
}Response (422):
{
"errors": {
"cost_price": [
"Cost price should not be higher than selling price (negative margin)"
],
"reserved_quantity": [
"Reserved quantity cannot exceed stock quantity"
]
}
}Nested Field Validation
For nested objects (arrays, JSON fields), errors are dot-notated:
Request:
{
"name": "Product",
"sku": "TEST-001",
"selling_price": 100,
"images": [
{
"url": "not-a-url",
"is_primary": true
},
{
"url": "also-not-a-url",
"is_primary": true
}
],
"attributes": [
{
"code": "non-existent-attribute",
"value": "test"
}
]
}Response (422):
{
"errors": {
"images.0.url": [
"The images.0.url must be a valid URL"
],
"images.1.url": [
"The images.1.url must be a valid URL"
],
"images": [
"Only one image can be set as primary"
],
"attributes.0.code": [
"The specified attribute does not exist"
]
}
}Handling Errors in Your Application
Basic Error Handling
async function createProduct(data) {
try {
const response = await fetch('https://api.synthesq.com/api/v1/operations/products', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(data)
});
// Check if response is successful
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Request failed');
}
return await response.json();
} catch (error) {
console.error('Error creating product:', error);
throw error;
}
}Handling Validation Errors
async function createProduct(data) {
try {
const response = await fetch('https://api.synthesq.com/api/v1/operations/products', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (response.status === 422) {
const { errors } = await response.json();
// Display validation errors to user
Object.keys(errors).forEach(field => {
errors[field].forEach(message => {
console.error(`${field}: ${message}`);
// Display in UI: showFieldError(field, message);
});
});
return { success: false, errors };
}
if (!response.ok) {
throw new Error('Request failed');
}
const result = await response.json();
return { success: true, data: result.data };
} catch (error) {
console.error('Unexpected error:', error);
return { success: false, error: error.message };
}
}Retry Logic for Rate Limiting
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// Handle rate limiting
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
if (attempt < maxRetries) {
console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
await sleep(retryAfter * 1000);
continue;
}
throw new Error('Rate limit exceeded');
}
return response;
} catch (error) {
if (attempt === maxRetries) throw error;
// Exponential backoff for server errors
const delay = Math.pow(2, attempt) * 1000;
await sleep(delay);
}
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}Best Practices
1. Always Check Status Codes
Don't assume a request succeeded. Always check the HTTP status code.
if (!response.ok) {
// Handle error
}2. Handle Validation Errors Gracefully
Display field-specific validation errors to users in a helpful way.
// Good: Show error next to the field
showFieldError('sku', 'SKU already exists');
// Bad: Generic alert
alert('Something went wrong');3. Implement Retry Logic
For transient errors (500, 503) and rate limiting (429), implement retry logic with exponential backoff.
4. Log Errors for Debugging
Log full error responses (in development) to help diagnose issues:
console.error('API Error:', {
status: response.status,
body: await response.json(),
url: response.url
});5. Handle Token Expiration
When you receive a 401 Unauthorized, refresh the access token or redirect to login:
if (response.status === 401) {
// Token expired - refresh or re-authenticate
await refreshToken();
// Retry the original request
}6. Provide User-Friendly Messages
Translate technical errors into user-friendly messages:
const userFriendlyMessages = {
401: 'Please log in again',
403: 'You don\'t have permission to do this',
404: 'Item not found',
422: 'Please check your input',
500: 'Something went wrong. Please try again later',
503: 'Service is temporarily unavailable'
};
const message = userFriendlyMessages[response.status] || 'An error occurred';Next Steps
- Review Making Requests for request structure
- Learn about Authentication for token management
- Explore the API Reference for endpoint-specific errors