WebSocket API Errors
This page describes the error handling patterns for the Hercle WebSocket API, which uses SignalR for real-time communication.
Error Response Format
WebSocket errors are communicated through the subject field in hub responses. When an error occurs, the subject will indicate the error type rather than the expected success subject.
Hub Response Structure
All hub responses follow this structure:
{
"channel": "eventName",
"topic": "topicName",
"subject": "topic.subject",
"type": "message",
"clientId": "clientId",
"data": "{...}"
}
Error Subjects
The Hercle WebSocket API uses the following error subjects:
-
preChecksError- Request failed mandatory validation checks- Occurs when command arguments are incorrect, missing, or in wrong format
- Common causes: Invalid data types, missing required fields, out-of-range values
- Solution: Verify all command arguments match the expected format and types
-
*.error- General operational or account error- Indicates issues with the operation execution or account state
- Details provided in
statusErrorormessagefield within the data - Examples: Insufficient funds, invalid API key, order rejection
Common Error Scenarios
Authentication Errors
Invalid API Key
- Cause: API key is incorrect, expired, or not provided
- Subject: Connection will fail to establish or disconnect immediately
- Solution: Verify your API key from the Account page and ensure it's correctly passed in the
accessTokenFactory
Unauthorized Access
- Cause: API key doesn't have required permissions
- Subject:
*.error - Solution: Check your account KYC status and API key permissions
Validation Errors
Missing Required Arguments
- Cause: Command invoked without all required arguments
- Subject:
preChecksError - Data: Error message explaining missing field
- Solution: Review command documentation and provide all required arguments
Invalid Argument Format
- Cause: Argument value doesn't match expected type or format
- Subject:
preChecksError - Examples:
- String provided where number expected
- Invalid date format
- Malformed pair name
- Solution: Ensure arguments match the documented types
Out of Range Values
- Cause: Numeric values exceed allowed limits
- Subject:
preChecksError - Examples:
- Size below
minSizeor abovemaxSize - Slippage percentage invalid
- ClientId exceeds 36 characters
- Size below
- Solution: Check pair limits using
GetPairscommand
Order Errors
Insufficient Funds
- Cause: Account balance too low for trade
- Subject:
execution.order.executed - Data:
{ "status": 2, "statusError": "Insufficient funds" } - Solution: Check balances using
GetBalancescommand
Order Rejected
- Cause: Order couldn't be executed due to market conditions
- Subject:
execution.order.executed - Data:
{ "status": 2, "statusError": "Order rejected" } - Solution: Review order parameters and current market conditions
Invalid Price ID
- Cause: Price update ID expired or invalid
- Subject:
preChecksErroror*.error - Solution: Subscribe to fresh price updates and use recent IDs
Subscription Errors
Already Subscribed
- Cause: Attempting to subscribe to a channel already active
- Subject:
preChecksError - Solution: Track active subscriptions to avoid duplicates
Invalid Subscription Parameters
- Cause: Subscription parameters don't match requirements
- Subject:
preChecksError - Examples:
- Invalid pair name
- Invalid size value
- Solution: Verify pair exists using
GetPairsand size is within limits
Connection Errors
SignalR Connection Issues
Connection Failed
- Causes:
- Network connectivity problems
- Invalid hub URL or path
- CORS issues (browser environments)
- Invalid API key
- Detection:
connection.start()promise rejects - Solution: Verify base URL and hub path, check network, validate API key
Connection Dropped
- Causes:
- Network interruption
- Server maintenance window
- Client inactivity timeout
- Detection:
connection.oncloseevent fires - Solution: Implement automatic reconnection with exponential backoff
Reconnection Failures
- Causes:
- Persistent network issues
- Server unavailable
- Invalid credentials after reconnection
- Detection: Multiple failed reconnection attempts
- Solution: Exponential backoff with maximum retry limit
Maintenance Windows
HercleX has scheduled maintenance that may cause disconnections:
- Regular Maintenance: Every Wednesday, 10:00 AM - 11:00 AM GMT+1
- Urgent Upgrades: Communicated in advance when possible
- Impact: SignalR disconnection during upgrade window
Recommended Handling:
- Implement automatic reconnection policy
- Store command state to retry after reconnection
- Subscribe to status updates at status.hercle.financial
Error Handling Best Practices
1. Implement Reconnection Logic
connection.onclose(async (error) => {
console.log('Disconnected from SignalR. Attempting to reconnect...');
let retryCount = 0;
const maxRetries = 10;
const baseDelay = 1000; // 1 second
while (retryCount < maxRetries) {
try {
// Exponential backoff: 1s, 2s, 4s, 8s, ...
const delay = baseDelay * Math.pow(2, retryCount);
await new Promise((resolve) => setTimeout(resolve, delay));
await connection.start();
console.log('Reconnected successfully');
// Re-subscribe to all active channels
await resubscribeToChannels();
break;
} catch (err) {
retryCount++;
console.error(`Reconnection attempt ${retryCount} failed:`, err);
if (retryCount >= maxRetries) {
console.error('Max reconnection attempts reached');
// Notify user or trigger alert
}
}
}
});
2. Handle Command Errors
async function sendCommand(commandName, args) {
try {
await connection.invoke(commandName, args);
} catch (err) {
console.error(`Command ${commandName} failed:`, err);
// Check if connection is still alive
if (connection.state !== signalR.HubConnectionState.Connected) {
console.log('Connection lost, attempting to reconnect...');
await connection.start();
// Retry command after reconnection
await connection.invoke(commandName, args);
} else {
// Command failed for other reasons
throw err;
}
}
}
3. Parse Hub Response Errors
connection.on('executionEvents', (message) => {
const response = JSON.parse(message);
// Check for error subjects
if (response.subject === 'preChecksError') {
const errorData = JSON.parse(response.data);
console.error('Pre-check validation failed:', errorData.message);
// Handle validation error (show to user, log, etc.)
return;
}
if (response.subject.endsWith('.error')) {
const errorData = JSON.parse(response.data);
console.error(
'Operation error:',
errorData.message || errorData.statusError
);
// Handle operation error
return;
}
// Process successful response
const data = JSON.parse(response.data);
// For orders, check status field
if (data.status === 2) {
console.error('Order cancelled/rejected:', data.statusError);
// Handle order rejection
return;
}
// Success - process data
console.log('Received data:', data);
});
4. Validate Before Sending
async function placeOtcOrder(clientId, priceId, side, slippage) {
// Validate arguments before sending
if (!clientId || clientId.length > 36) {
throw new Error('Invalid clientId: must be provided and max 36 characters');
}
if (!['buy', 'sell'].includes(side)) {
throw new Error('Invalid side: must be "buy" or "sell"');
}
if (typeof slippage !== 'number' || slippage < 0) {
throw new Error('Invalid slippage: must be a positive number');
}
// Send command
await connection.invoke('PlaceOtcOrder', [clientId, priceId, side, slippage]);
}
5. Monitor Connection State
// Check connection state before sending commands
function ensureConnected() {
if (connection.state !== signalR.HubConnectionState.Connected) {
throw new Error('Not connected to SignalR hub');
}
}
// Track connection state changes
connection.onreconnecting((error) => {
console.log('Connection lost, reconnecting...', error);
// Update UI to show reconnecting state
});
connection.onreconnected((connectionId) => {
console.log('Reconnected with connection ID:', connectionId);
// Update UI to show connected state
// Resubscribe to channels
});
Testing Error Scenarios
Sandbox Environment
Use the sandbox environment to test error handling:
- Base URL:
https://publicapi.sandbox.hercle.financial - Hub Path:
ExecutionLiveServer/v1
Test Cases
Test Invalid Arguments:
// Should trigger preChecksError
await connection.invoke('PlaceOtcOrder', [
'clientId',
'invalidPriceId',
'invalidSide', // Should be 'buy' or 'sell'
-1, // Invalid slippage
]);
Test Insufficient Funds:
// First, get available pairs
await connection.invoke('GetPairs', ['clientId']);
// Subscribe to large price update
await connection.invoke('SubscribePairPriceChannel', [
'clientId',
'BTCUSDT',
1000, // Very large size
]);
// Try to place order (should fail with insufficient funds)
Test Connection Recovery:
// Manually close connection
await connection.stop();
// Verify reconnection logic triggers
// Check that subscriptions are restored
Test Expired Price ID:
// Get a price update
let priceId;
connection.on('subscriptions', (message) => {
const data = JSON.parse(JSON.parse(message).data);
priceId = data.id;
});
await connection.invoke('SubscribePairPriceChannel', [
'clientId',
'BTCUSDT',
0.01,
]);
// Wait for price to expire (typically a few seconds)
await new Promise((resolve) => setTimeout(resolve, 10000));
// Try to use expired price ID
await connection.invoke('PlaceOtcOrder', ['clientId', priceId, 'buy', 0.1]);
// Should receive error
Additional Resources
- SignalR Documentation: Microsoft SignalR Overview
- Connection Lifetime Events: Handling Connection Lifetime
- API Status Page: status.hercle.financial
- Authentication Guide: See WebSocket Authentication for API key setup