Complete guide to error handling in the LISA REST API.
The LISA REST API uses a consistent error response format and HTTP status codes to communicate errors clearly. All error responses follow the same structure for easy handling.
All error responses follow this consistent format:
{
"success": false,
"error": "Error message",
"details": [
{
"field": "fieldName",
"message": "Specific validation error message"
}
],
"timestamp": "2024-12-01T08:47:16.838Z"
}
| Code | Description | When Used |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH requests |
| 201 | Created | Successful POST requests |
| 400 | Bad Request | Validation errors, malformed requests |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Valid authentication but insufficient permissions |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side errors |
Status: 401 Unauthorized
{
"success": false,
"error": "Authorization header is required",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
Authorization header providedAuthorization headerHow to fix:
Authorization: Bearer <jwt-token> headerStatus: 401 Unauthorized
{
"success": false,
"error": "Invalid authorization header format",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
Authorization headerHow to fix:
Authorization: Bearer <jwt-token>Status: 401 Unauthorized
{
"success": false,
"error": "Token has expired",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
/tokens endpointStatus: 401 Unauthorized
{
"success": false,
"error": "Invalid token",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 403 Forbidden
{
"success": false,
"error": "Access denied: Invalid integratorUID",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 403 Forbidden
{
"success": false,
"error": "Access denied: You can only access your own data",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 400 Bad Request
{
"success": false,
"error": "Validation failed",
"details": [
{
"field": "integratorUID",
"message": "\"integratorUID\" is required"
}
],
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 400 Bad Request
{
"success": false,
"error": "Validation failed",
"details": [
{
"field": "MAC",
"message": "\"MAC\" must be a valid MAC address"
}
],
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 400 Bad Request
{
"success": false,
"error": "Invalid limit. Must be a number between 1 and 100",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 404 Not Found
{
"success": false,
"error": "User not found",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 404 Not Found
{
"success": false,
"error": "Not Found",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
Status: 429 Too Many Requests
{
"success": false,
"error": "Too many requests from this IP, please try again later.",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
There are two independent 429 limiters:
Too many requests from this IP, please try again later.Daily quota exceeded for this endpoint ....Quota response headers (sent on every authenticated request):
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Per-endpoint daily call cap for your tier (unlimited for Enterprise) |
X-RateLimit-Remaining | Calls remaining for this endpoint today (0 when over quota) |
X-RateLimit-Reset | Unix epoch seconds of the next 00:00 UTC reset |
How to fix:
X-RateLimit-Reset)X-RateLimit-* headersStatus: 403 Forbidden
Two distinct causes, distinguishable by the message:
| Message starts with | Cause | Fix |
|---|---|---|
Raw sensor data is not available to user tokens | You authenticated with a user token (POST /tokens/login). Raw data is not reachable from a user token at any tier. | Use an integrator token (POST /tokens) on the Pro or Enterprise tier. Do not request a tier change — it cannot help. |
Raw sensor data access requires the Pro or Enterprise API tier | You used an integrator token, but the integrator is on Free or Starter. The message names the resolved tier. | Contact BioCV to upgrade the integrator account. |
Both apply to .../animals/{animalMac}/sensordata, .../sensordata/latest and .../sensordata/analytics.
Status: 500 Internal Server Error
{
"success": false,
"error": "Internal server error",
"timestamp": "2024-12-01T08:47:16.838Z"
}
When it occurs:
How to fix:
async function makeAPIRequest(url, options = {}) {
try {
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
...options.headers
},
...options
});
const data = await response.json();
if (!data.success) {
// Handle API errors
if (response.status === 401) {
// Token expired, try to refresh
await refreshToken();
return makeAPIRequest(url, options); // Retry with new token
}
if (response.status === 429) {
// Rate limited, implement backoff
await new Promise(resolve => setTimeout(resolve, 1000));
return makeAPIRequest(url, options);
}
throw new Error(`API Error: ${data.error}`);
}
return data;
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}
import requests
import time
from typing import Dict, Any
class LISAPIClient:
def make_request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
try:
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
response = requests.request(method, endpoint, headers=headers, **kwargs)
data = response.json()
if not data.get("success", False):
if response.status_code == 401:
# Token expired, refresh and retry
self.refresh_token()
return self.make_request(method, endpoint, **kwargs)
if response.status_code == 429:
# Rate limited, wait and retry
time.sleep(1)
return self.make_request(method, endpoint, **kwargs)
raise Exception(f"API Error: {data.get('error', 'Unknown error')}")
return data
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
raise
async function handleTokenExpiry() {
try {
const response = await fetch('/api/v1/tokens/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: refreshToken })
});
const data = await response.json();
if (data.success) {
accessToken = data.data.accessToken;
refreshToken = data.data.refreshToken;
return true;
}
} catch (error) {
console.error('Token refresh failed:', error);
}
// If refresh fails, generate new token
return generateNewToken();
}
async function makeRequestWithRetry(url, options = {}, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
// Rate limited, wait with exponential backoff
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return response;
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
function logError(error, context = {}) {
console.error('API Error:', {
message: error.message,
status: error.status,
timestamp: new Date().toISOString(),
context: context
});
// Send to monitoring service
if (window.monitoring) {
window.monitoring.captureException(error, {
extra: context
});
}
}
function checkRateLimit(response) {
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
if (remaining && parseInt(remaining) < 10) {
console.warn(`Rate limit warning: ${remaining} requests remaining`);
}
return {
remaining: parseInt(remaining) || null,
reset: parseInt(reset) || null
};
}
Problem: Token expires while processing a large dataset.
Solution:
async function processLargeDataset(userId) {
let page = 1;
let allData = [];
while (true) {
try {
const response = await fetch(`/api/v1/users/${userId}/animals?page=${page}&limit=100`, {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const data = await response.json();
if (!data.success) {
if (response.status === 401) {
await refreshToken();
continue; // Retry with new token
}
throw new Error(data.error);
}
allData.push(...data.data);
if (page >= data.pagination.totalPages) break;
page++;
} catch (error) {
console.error('Error processing page:', page, error);
throw error;
}
}
return allData;
}
Problem: User submits invalid data.
Solution:
async function createUser(userData) {
try {
const response = await fetch('/api/v1/users', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
const data = await response.json();
if (!data.success) {
if (response.status === 400 && data.details) {
// Handle validation errors
data.details.forEach(detail => {
console.error(`Field ${detail.field}: ${detail.message}`);
});
return { success: false, validationErrors: data.details };
}
throw new Error(data.error);
}
return { success: true, data: data.data };
} catch (error) {
console.error('Failed to create user:', error);
return { success: false, error: error.message };
}
}
Problem: Network connectivity issues.
Solution:
async function makeRequestWithRetry(url, options = {}, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
return response;
} catch (error) {
if (attempt === maxRetries) {
throw new Error(`Request failed after ${maxRetries} attempts: ${error.message}`);
}
// Wait before retry (exponential backoff)
const delay = Math.pow(2, attempt - 1) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
const response = await fetch(url, options);
console.log('Status:', response.status);
console.log('Headers:', Object.fromEntries(response.headers.entries()));
function validateRequestData(data, schema) {
const errors = [];
if (schema.required) {
schema.required.forEach(field => {
if (!data[field]) {
errors.push(`${field} is required`);
}
});
}
return errors;
}
async function testToken() {
try {
const response = await fetch('/api/v1/tokens/validate', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const data = await response.json();
console.log('Token valid:', data.success);
return data.success;
} catch (error) {
console.error('Token validation failed:', error);
return false;
}
}
Next: Code Examples - Complete implementation examples
Diese Seite wird aus dem offiziellen LiSA-API-Wiki erzeugt. Sollte etwas falsch sein oder fehlen, kontaktieren Sie uns — wir korrigieren es an der Quelle.
Wiki-Stand e01e79e · 19.8.2026