Error Handling
vatnode uses standard HTTP status codes and returns detailed error information in JSON format.
Error Response Format
All errors follow this structure:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"requestId": "req_abc123",
"details": {}
}
}| Field | Type | Description |
|---|---|---|
| code | string | Machine-readable error code |
| message | string | Human-readable description |
| requestId | string | Unique request ID for debugging |
| details | object | Additional context (optional) |
HTTP Status Codes
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid input |
| 401 | Unauthorized - Invalid or missing API key |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error |
| 502 | Bad Gateway - Upstream error (VIES) |
| 503 | Service Unavailable |
| 504 | Gateway Timeout - VIES timeout |
Error Codes
INVALID_INPUT
The request contains invalid parameters.
{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid VAT ID format",
"requestId": "req_abc123"
}
}INVALID_FORMAT
The VAT number format is invalid.
{
"error": {
"code": "INVALID_FORMAT",
"message": "Invalid VAT ID format. Expected format: country code (2 letters) followed by VAT number",
"requestId": "req_abc123"
}
}RATE_LIMITED
You've exceeded your rate limit.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please try again later.",
"retryAfter": 30,
"requestId": "req_abc123"
}
}How to handle:
- Check the
Retry-Afterheader for when to retry - Implement exponential backoff
- Consider upgrading your plan for higher limits
VIES_UNAVAILABLE
The VIES service is temporarily unavailable.
{
"error": {
"code": "VIES_UNAVAILABLE",
"message": "VIES service is temporarily unavailable",
"viesCode": "SERVICE_UNAVAILABLE",
"requestId": "req_abc123"
}
}VIES_ERROR
An error occurred while communicating with VIES.
{
"error": {
"code": "VIES_ERROR",
"message": "An error occurred while validating VAT",
"viesCode": "MS_UNAVAILABLE",
"requestId": "req_abc123"
}
}VIES-specific codes returned in the viesCode field:
| viesCode | Description |
|---|---|
| MS_UNAVAILABLE | Member state's national service is down or not responding |
| MS_MAX_CONCURRENT_REQ | Member state's service is busy; max concurrent requests exceeded |
| MS_MAX_CONCURRENT_REQ_TIME | Member state's service is busy; concurrent request timed out |
| GLOBAL_MAX_CONCURRENT_REQ | VIES global concurrent request limit exceeded |
| GLOBAL_MAX_CONCURRENT_REQ_TIME | VIES global concurrent request timeout |
| SERVICE_UNAVAILABLE | Central VIES service is unavailable |
| TIMEOUT | VIES request timed out |
| INVALID_INPUT | VIES rejected the input (invalid country code or empty VAT number) |
| VAT_BLOCKED | The requested VAT number is blocked in VIES |
| IP_BLOCKED | The requesting IP address is blocked by VIES |
PLAN_LIMIT
Your monthly request quota has been reached.
{
"error": {
"code": "PLAN_LIMIT",
"message": "Monthly request quota exceeded. Upgrade your plan to continue.",
"requestId": "req_abc123"
}
}ALREADY_EXISTS
The resource you are trying to create already exists (e.g., duplicate API key label or webhook URL).
{
"error": {
"code": "ALREADY_EXISTS",
"message": "A resource with this identifier already exists.",
"requestId": "req_abc123"
}
}CREATION_FAILED
An internal error occurred while creating a resource. Retrying the request is safe.
{
"error": {
"code": "CREATION_FAILED",
"message": "Failed to create resource. Please try again.",
"requestId": "req_abc123"
}
}Best Practices
1. Always Check HTTP Status
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 400:
console.error('Invalid request:', error.error.message);
break;
case 429:
const retryAfter = response.headers.get('Retry-After');
console.error('Rate limited, retry after:', retryAfter);
break;
case 503:
console.error('Service unavailable, retrying...');
break;
default:
console.error('Error:', error.error.message);
}
}2. Implement Retry Logic
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) return response.json();
if (response.status === 429 || response.status >= 500) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, attempt);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
// Client error, don't retry
const error = await response.json();
throw new Error(error.error.message);
} catch (error) {
if (attempt === maxRetries) throw error;
}
}
}3. Log Request IDs
Always log the requestId from error responses. This helps us investigate issues:
try {
const result = await validateVat('IE6388047V');
} catch (error) {
console.error('Validation failed:', {
message: error.message,
requestId: error.requestId, // Include this in support tickets
});
}4. Handle VIES Downtime Gracefully
VIES has scheduled maintenance and occasional outages. Consider showing a user-friendly message:
async function validateVatWithFallback(vatId) {
try {
return await validateVat(vatId);
} catch (error) {
if (error.code === 'VIES_UNAVAILABLE') {
return {
valid: null, // Unknown
vatId,
message: 'VAT validation temporarily unavailable. Will verify later.',
pendingVerification: true,
};
}
throw error;
}
}Support
If you encounter persistent errors:
- Check the vatnode status page for known issues
- Review your API key and rate limits in the Dashboard
- Contact support with your
requestIdfor investigation