Error Handling
import { Callout } from 'nextra/components'
Error Handling
Understanding how to handle errors and edge cases when integrating with the Gett B2C API.
HTTP Status Codes
The API uses standard HTTP status codes to indicate the success or failure of requests:
Success Codes
Code | Description |
---|---|
200 | OK - Request successful |
201 | Created - Resource created successfully |
204 | No Content - Request successful, no response body (e.g., order cancellation) |
Client Error Codes
Code | Description | Common Causes |
---|---|---|
400 | Bad Request | Invalid JSON, missing required parameters, malformed data |
401 | Unauthorized | Invalid or expired token, missing Authorization header |
402 | Payment Required | Insufficient funds, payment method issues |
403 | Forbidden | Access denied, invalid permissions |
404 | Not Found | Invalid endpoint, order not found, product unavailable |
422 | Unprocessable Entity | Valid JSON but business logic validation failed |
429 | Too Many Requests | Rate limit exceeded |
Server Error Codes
Code | Description |
---|---|
500 | Internal Server Error |
502 | Bad Gateway |
503 | Service Unavailable |
504 | Gateway Timeout |
Error Response Format
All error responses follow a consistent format:
{
"status": "error",
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error description",
"details": {
"field": "Specific field validation error"
}
},
"request_id": "uuid-for-debugging"
}
Common Error Scenarios
Authentication Errors
Invalid Credentials (401)
{
"status": "error",
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Invalid client credentials provided"
}
}
Solution: Verify your client_id
and client_secret
are correct.
Expired Token (401)
{
"status": "error",
"error": {
"code": "TOKEN_EXPIRED",
"message": "The access token has expired"
}
}
Solution: Refresh your token using the OAuth endpoint.
Validation Errors
Missing Required Fields (400)
{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Required fields are missing",
"details": {
"partner_id": "This field is required",
"user_accepted_terms_and_privacy": "This field is required"
}
}
}
Invalid Coordinates (422)
{
"status": "error",
"error": {
"code": "INVALID_LOCATION",
"message": "Invalid coordinates provided",
"details": {
"stops[0].location.lat": "Latitude must be between -90 and 90"
}
}
}
Invalid Phone Number (422)
{
"status": "error",
"error": {
"code": "INVALID_PHONE",
"message": "Phone number format is invalid",
"details": {
"stops[0].actions[0].user.phone": "Phone number must be in international format without + sign"
}
}
}
Business Logic Errors
Product Not Available (404)
{
"status": "error",
"error": {
"code": "PRODUCT_UNAVAILABLE",
"message": "The requested product is not available for this route"
}
}
Terms Not Accepted (422)
{
"status": "error",
"error": {
"code": "TERMS_NOT_ACCEPTED",
"message": "User must accept terms and privacy policy"
}
}
Invalid Schedule Time (422)
{
"status": "error",
"error": {
"code": "INVALID_SCHEDULE",
"message": "Scheduled time must be in the future",
"details": {
"scheduled_at": "Cannot schedule orders in the past"
}
}
}
Error Handling Best Practices
Implement Retry Logic
For transient errors (500, 502, 503, 504), implement exponential backoff:
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return await response.json();
}
// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
throw new Error(`Client error: ${response.status}`);
}
// Retry server errors (5xx)
if (attempt === maxRetries) {
throw new Error(`Server error after ${maxRetries} attempts`);
}
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
}
}
}
Handle Token Expiration
Automatically refresh tokens when they expire:
class GettAPIClient {
async makeAuthenticatedRequest(url, options) {
try {
const response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.token}`
}
});
if (response.status === 401) {
// Token expired, refresh and retry
await this.authenticate();
return await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.token}`
}
});
}
return response;
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
}
Validate Input Before Sending
Prevent common validation errors by validating input client-side:
function validateOrderData(orderData) {
const errors = [];
// Required fields
if (!orderData.partner_id) {
errors.push('partner_id is required');
}
if (!orderData.user_accepted_terms_and_privacy) {
errors.push('user_accepted_terms_and_privacy must be true');
}
// Validate coordinates
orderData.stops?.forEach((stop, index) => {
const lat = stop.location?.lat;
const lng = stop.location?.lng;
if (lat < -90 || lat > 90) {
errors.push(`stops[${index}].location.lat must be between -90 and 90`);
}
if (lng < -180 || lng > 180) {
errors.push(`stops[${index}].location.lng must be between -180 and 180`);
}
});
// Validate phone numbers
orderData.stops?.forEach((stop, stopIndex) => {
stop.actions?.forEach((action, actionIndex) => {
const phone = action.user?.phone;
if (phone && !/^\d{10,15}$/.test(phone)) {
errors.push(`stops[${stopIndex}].actions[${actionIndex}].user.phone must be 10-15 digits`);
}
});
});
return errors;
}
Graceful Error Display
Present errors to users in a helpful way:
function handleApiError(error, response) {
switch (response.status) {
case 400:
return 'Please check your request and try again.';
case 401:
return 'Authentication failed. Please try logging in again.';
case 402:
return 'Payment is required to complete this request.';
case 404:
return 'The requested service is not available for this route.';
case 422:
if (error.error?.details) {
const fieldErrors = Object.values(error.error.details);
return `Please correct the following: ${fieldErrors.join(', ')}`;
}
return 'Please check your information and try again.';
case 429:
return 'Too many requests. Please wait a moment and try again.';
case 500:
case 502:
case 503:
case 504:
return 'Service temporarily unavailable. Please try again later.';
default:
return 'An unexpected error occurred. Please try again.';
}
}
Debugging Tips
Request IDs
Save the request_id
from error responses for support requests:
async function logApiError(error, response) {
const errorData = await response.json();
console.error('API Error:', {
status: response.status,
code: errorData.error?.code,
message: errorData.error?.message,
requestId: errorData.request_id,
timestamp: new Date().toISOString()
});
}
Network Issues
Handle network connectivity problems:
async function makeRequest(url, options) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
return response;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timed out');
} else if (error.name === 'TypeError') {
throw new Error('Network error - please check your connection');
}
throw error;
}
}
Updated 7 days ago