Error Codes & Handling
Understand Nexgent API errors and how to handle them in your application.
Error Response Format
All API errors return a consistent JSON structure:
{
"success": false,
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": { ... }
}| Field | Type | Description |
|---|---|---|
success | boolean | Always false for errors |
error | string | Human-readable error message |
code | string | Machine-readable error code (optional) |
details | object | Additional error details (optional) |
In development mode, errors also include a stack field with the stack trace.
HTTP Status Codes
Success Codes
| Code | Meaning | Usage |
|---|---|---|
200 | OK | Successful GET, PUT, DELETE |
201 | Created | Successful POST creating a resource |
Client Error Codes
| Code | Meaning | Common Causes |
|---|---|---|
400 | Bad Request | Invalid input, validation failure |
401 | Unauthorized | Missing or invalid token |
403 | Forbidden | Valid token but insufficient permissions |
404 | Not Found | Resource doesn't exist |
409 | Conflict | Duplicate resource, constraint violation |
423 | Locked | Account locked due to failed attempts |
429 | Too Many Requests | Rate limit exceeded |
Server Error Codes
| Code | Meaning | Common Causes |
|---|---|---|
500 | Internal Server Error | Unexpected server error |
502 | Bad Gateway | External service failure (Jupiter, Pyth) |
503 | Service Unavailable | Server overloaded or maintenance |
Error Codes Reference
Authentication Errors
| Code | HTTP | Description |
|---|---|---|
AUTHENTICATION_ERROR | 401 | Token invalid, expired, or missing |
AUTHORIZATION_ERROR | 403 | User lacks permission for action |
Examples:
// Missing token
{
"error": "Authorization header required"
}
// Invalid token
{
"error": "Invalid token"
}
// Expired token
{
"error": "Token expired"
}
// Blacklisted token (after logout)
{
"error": "Token has been revoked"
}
// Wrong token type
{
"error": "Invalid token type"
}Validation Errors
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Request validation failed |
Examples:
// Missing required field
{
"error": "Name is required"
}
// Invalid format
{
"error": "Invalid agent ID format"
}
// Out of range
{
"error": "Signal strength must be between 1 and 5"
}
// Invalid enum value
{
"error": "Trading mode must be either 'simulation' or 'live'"
}
// Password too weak
{
"error": "Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
}Resource Errors
| Code | HTTP | Description |
|---|---|---|
NOT_FOUND | 404 | Requested resource not found |
CONFLICT | 409 | Resource conflict (duplicate) |
Examples:
// Resource not found
{
"error": "Agent not found"
}
// Email already exists
{
"error": "Email already registered"
}
// Duplicate API key name
{
"error": "API key name already exists"
}Trading Errors
| Code | HTTP | Description |
|---|---|---|
TRADING_ERROR | 400 | Trading operation failed |
Examples:
// Insufficient balance
{
"error": "Insufficient SOL balance",
"code": "TRADING_ERROR",
"details": {
"required": 1.5,
"available": 0.5
}
}
// Position not found
{
"error": "Position not found",
"code": "TRADING_ERROR"
}
// Invalid slippage
{
"error": "Slippage exceeds maximum allowed",
"details": {
"requested": 5000,
"maximum": 1000
}
}External Service Errors
| Code | HTTP | Description |
|---|---|---|
EXTERNAL_SERVICE_ERROR | 502 | External dependency failed |
Examples:
// Jupiter API failure
{
"error": "Jupiter error: Failed to get quote",
"code": "EXTERNAL_SERVICE_ERROR",
"details": {
"service": "jupiter",
"statusCode": 500
}
}
// Price feed unavailable
{
"error": "DexScreener error: Rate limited",
"code": "EXTERNAL_SERVICE_ERROR"
}Rate Limiting Errors
| HTTP | Description |
|---|---|
429 | Too many requests |
Response:
{
"error": "Too many requests. Please try again later.",
"retryAfter": 45
}| Field | Type | Description |
|---|---|---|
retryAfter | number | Seconds until rate limit resets |
Rate Limits:
| Endpoint | Limit | Window |
|---|---|---|
POST /trading-signals | 120 requests | 1 minute |
GET /data-sources | 30 requests | 1 minute |
/wallets/* | 5 requests | 1 minute |
Account Lockout
| HTTP | Description |
|---|---|
423 | Account locked |
Response:
{
"error": "Account is locked. Please try again later.",
"lockedUntil": "2025-01-20T10:45:00.000Z"
}Lockout Rules:
- Triggered after 5 failed login attempts
- Duration: 15 minutes
- Resets after successful login
Handling Errors
JavaScript/TypeScript
async function apiRequest(url: string, options: RequestInit) {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAccessToken()}`,
...options.headers,
},
});
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 401:
// Token expired - try refresh
if (error.error === 'Token expired') {
await refreshTokens();
return apiRequest(url, options); // Retry
}
// Invalid token - redirect to login
redirectToLogin();
break;
case 403:
// Permission denied
showError('You do not have permission for this action');
break;
case 404:
// Resource not found
showError(error.error || 'Resource not found');
break;
case 429:
// Rate limited
const retryAfter = error.retryAfter || 60;
showError(`Rate limited. Retry in ${retryAfter} seconds`);
break;
case 502:
// External service error
showError('Service temporarily unavailable. Please try again.');
break;
default:
// Generic error
showError(error.error || 'An error occurred');
}
throw new ApiError(error.error, response.status, error.code, error.details);
}
return response.json();
}
class ApiError extends Error {
constructor(
message: string,
public statusCode: number,
public code?: string,
public details?: any
) {
super(message);
this.name = 'ApiError';
}
}React Query Error Handling
import { QueryClient } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: (failureCount, error) => {
// Don't retry on 4xx errors (except 429)
if (error instanceof ApiError) {
if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {
return false;
}
}
return failureCount < 3;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
},
mutations: {
onError: (error) => {
if (error instanceof ApiError) {
// Handle specific errors
if (error.statusCode === 401) {
// Session expired
signOut();
}
}
},
},
},
});Python Error Handling
import requests
from dataclasses import dataclass
from typing import Optional, Any
@dataclass
class ApiError(Exception):
message: str
status_code: int
code: Optional[str] = None
details: Optional[Any] = None
def api_request(method: str, url: str, **kwargs) -> dict:
headers = kwargs.pop('headers', {})
headers['Content-Type'] = 'application/json'
headers['Authorization'] = f'Bearer {get_access_token()}'
response = requests.request(method, url, headers=headers, **kwargs)
if not response.ok:
error_data = response.json()
if response.status_code == 401 and error_data.get('error') == 'Token expired':
refresh_tokens()
return api_request(method, url, **kwargs)
raise ApiError(
message=error_data.get('error', 'Unknown error'),
status_code=response.status_code,
code=error_data.get('code'),
details=error_data.get('details'),
)
return response.json()
# Usage
try:
agent = api_request('GET', f'{BASE_URL}/api/v1/agents/{agent_id}')
except ApiError as e:
if e.status_code == 404:
print(f"Agent not found: {e.message}")
elif e.status_code == 429:
print("Rate limited, waiting...")
else:
print(f"Error: {e.message}")Best Practices
1. Always Check Status Codes
Don't assume success. Check the HTTP status code before parsing the response body.
2. Handle Token Expiration Gracefully
Implement automatic token refresh to avoid disrupting user experience.
3. Show User-Friendly Messages
Map technical errors to user-friendly messages:
const errorMessages: Record<string, string> = {
'AUTHENTICATION_ERROR': 'Please log in again',
'AUTHORIZATION_ERROR': 'You don\'t have permission for this action',
'NOT_FOUND': 'The requested item was not found',
'TRADING_ERROR': 'Trade could not be completed',
'EXTERNAL_SERVICE_ERROR': 'Service temporarily unavailable',
};4. Log Errors for Debugging
Log errors with context for debugging:
console.error('API Error:', {
url: response.url,
status: response.status,
error: error.error,
code: error.code,
details: error.details,
});5. Implement Retry Logic
For transient errors (5xx, 429), implement exponential backoff:
async function retryWithBackoff(fn: () => Promise<any>, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries - 1) throw error;
const isRetryable = error instanceof ApiError &&
(error.statusCode >= 500 || error.statusCode === 429);
if (!isRetryable) throw error;
const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
await new Promise(r => setTimeout(r, delay));
}
}
}