Skip to content

Error Handling

Igor Sazonov edited this page Mar 15, 2026 · 1 revision

A guide to handling errors in the OKX PHP SDK.

Exception Hierarchy

The SDK provides a set of specialized exceptions for different error categories:

OKXException (base)
├── AuthenticationException
├── RateLimitException
├── InvalidParameterException
└── InsufficientFundsException

Base Exception

OKXException

The base class for all OKX API exceptions.

use Tigusigalpa\OKX\Exceptions\OKXException;

try {
    $balance = $client->account()->getBalance();
} catch (OKXException $e) {
    echo "OKX error code: " . $e->okxCode;
    echo "Message: " . $e->getMessage();
    echo "Raw response: " . $e->rawResponse;
}

Properties:

  • okxCode — OKX error code
  • rawResponse — Full raw response from the API

Specialized Exceptions

AuthenticationException

Thrown for authentication failures (codes 50111, 50113).

use Tigusigalpa\OKX\Exceptions\AuthenticationException;

try {
    $balance = $client->account()->getBalance();
} catch (AuthenticationException $e) {
    // Invalid API key or signature
    Log::error('OKX Authentication failed', [
        'code' => $e->okxCode,
        'message' => $e->getMessage()
    ]);
    
    // Notify the administrator
    Mail::to('admin@example.com')->send(new AuthFailedMail($e));
}

Common causes:

  • Invalid API key
  • Invalid secret key
  • Invalid passphrase
  • Expired API key
  • IP address not on the whitelist

RateLimitException

Thrown when the request rate limit is exceeded (code 50011).

use Tigusigalpa\OKX\Exceptions\RateLimitException;

try {
    $order = $client->trade()->placeOrder(...);
} catch (RateLimitException $e) {
    // Wait and retry
    sleep(1);
    
    try {
        $order = $client->trade()->placeOrder(...);
    } catch (RateLimitException $e) {
        Log::warning('Rate limit exceeded twice', [
            'endpoint' => 'placeOrder'
        ]);
        throw $e;
    }
}

Handling strategies:

  1. Exponential Backoff
function retryWithBackoff(callable $callback, int $maxRetries = 3): mixed
{
    $attempt = 0;
    
    while ($attempt < $maxRetries) {
        try {
            return $callback();
        } catch (RateLimitException $e) {
            $attempt++;
            if ($attempt >= $maxRetries) {
                throw $e;
            }
            sleep(pow(2, $attempt)); // 2, 4, 8 seconds
        }
    }
}

$order = retryWithBackoff(fn() => $client->trade()->placeOrder(...));
  1. Queue
// Dispatch to a queue instead of executing immediately
PlaceOrderJob::dispatch($instId, $side, $amount)
    ->onQueue('okx-orders');

InvalidParameterException

Thrown when request parameters are invalid (codes 51000–51099).

use Tigusigalpa\OKX\Exceptions\InvalidParameterException;

try {
    $order = $client->trade()->placeOrder(
        instId: 'INVALID-PAIR',
        tdMode: 'cash',
        side: 'buy',
        ordType: 'market',
        sz: '0.001'
    );
} catch (InvalidParameterException $e) {
    // Validation failed
    return response()->json([
        'error' => 'Invalid order parameters',
        'details' => $e->getMessage(),
        'code' => $e->okxCode
    ], 400);
}

Common errors:

  • Invalid instrument symbol
  • Order size below the minimum
  • Invalid order type
  • Missing required parameters

InsufficientFundsException

Thrown when the account has insufficient funds (codes 54000–54099).

use Tigusigalpa\OKX\Exceptions\InsufficientFundsException;

try {
    $order = $client->trade()->placeOrder(...);
} catch (InsufficientFundsException $e) {
    // Check the balance
    $balance = $client->account()->getBalance();
    
    return response()->json([
        'error' => 'Insufficient funds',
        'available_balance' => $balance[0]['details'][0]['availBal'],
        'required' => $requestedAmount
    ], 400);
}

Comprehensive Error Handling

Catching all exception types

use Tigusigalpa\OKX\Exceptions\{
    OKXException,
    AuthenticationException,
    RateLimitException,
    InvalidParameterException,
    InsufficientFundsException
};

try {
    $order = $client->trade()->placeOrder(
        instId: 'BTC-USDT',
        tdMode: 'cash',
        side: 'buy',
        ordType: 'market',
        sz: '100'
    );
    
    return response()->json(['success' => true, 'order' => $order]);
    
} catch (AuthenticationException $e) {
    Log::critical('OKX Authentication failed', ['error' => $e->getMessage()]);
    return response()->json(['error' => 'Authentication failed'], 401);
    
} catch (RateLimitException $e) {
    Log::warning('OKX Rate limit exceeded');
    return response()->json(['error' => 'Too many requests, please try again later'], 429);
    
} catch (InvalidParameterException $e) {
    Log::info('Invalid order parameters', ['error' => $e->getMessage()]);
    return response()->json(['error' => 'Invalid parameters: ' . $e->getMessage()], 400);
    
} catch (InsufficientFundsException $e) {
    Log::info('Insufficient funds for order');
    return response()->json(['error' => 'Insufficient funds'], 400);
    
} catch (OKXException $e) {
    Log::error('OKX API error', [
        'code' => $e->okxCode,
        'message' => $e->getMessage(),
        'response' => $e->rawResponse
    ]);
    return response()->json(['error' => 'API error: ' . $e->getMessage()], 500);
    
} catch (\Exception $e) {
    Log::error('Unexpected error', ['error' => $e->getMessage()]);
    return response()->json(['error' => 'Internal server error'], 500);
}

Laravel Exception Handler

Global handling in Handler.php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Tigusigalpa\OKX\Exceptions\OKXException;
use Throwable;

class Handler extends ExceptionHandler
{
    public function register(): void
    {
        $this->reportable(function (OKXException $e) {
            Log::channel('okx')->error('OKX API Error', [
                'code' => $e->okxCode,
                'message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
        });
        
        $this->renderable(function (OKXException $e, $request) {
            if ($request->expectsJson()) {
                return response()->json([
                    'error' => 'OKX API Error',
                    'code' => $e->okxCode,
                    'message' => $e->getMessage()
                ], 500);
            }
        });
    }
}

Retry Mechanism

Automatic retry service

class OKXRetryService
{
    public function executeWithRetry(
        callable $callback,
        int $maxAttempts = 3,
        int $delayMs = 1000
    ): mixed {
        $attempt = 0;
        $lastException = null;
        
        while ($attempt < $maxAttempts) {
            try {
                return $callback();
            } catch (RateLimitException $e) {
                $lastException = $e;
                $attempt++;
                
                if ($attempt < $maxAttempts) {
                    usleep($delayMs * 1000 * $attempt);
                    continue;
                }
            } catch (OKXException $e) {
                // Do not retry for other error types
                throw $e;
            }
        }
        
        throw $lastException;
    }
}

Usage:

$retryService = new OKXRetryService();

$order = $retryService->executeWithRetry(
    fn() => $client->trade()->placeOrder(...)
);

Pre-request Validation

Balance check

class OrderValidator
{
    public function __construct(private Client $okx) {}
    
    public function validateBalance(string $ccy, string $requiredAmount): bool
    {
        try {
            $balance = $this->okx->account()->getBalance($ccy);
            $available = (float) $balance[0]['details'][0]['availBal'];
            
            return $available >= (float) $requiredAmount;
        } catch (OKXException $e) {
            Log::error('Failed to check balance', ['error' => $e->getMessage()]);
            return false;
        }
    }
    
    public function validateOrderBeforePlacing(array $orderParams): array
    {
        $errors = [];
        
        // Validate the instrument
        try {
            $instruments = $this->okx->publicData()->getInstruments(
                instType: 'SPOT',
                instId: $orderParams['instId']
            );
            
            if (empty($instruments)) {
                $errors[] = 'Invalid instrument';
            }
        } catch (OKXException $e) {
            $errors[] = 'Failed to validate instrument';
        }
        
        // Validate balance for buy orders
        if ($orderParams['side'] === 'buy') {
            $quoteCcy = explode('-', $orderParams['instId'])[1];
            if (!$this->validateBalance($quoteCcy, $orderParams['sz'])) {
                $errors[] = 'Insufficient balance';
            }
        }
        
        return $errors;
    }
}

Error Monitoring

Sending to Sentry

use Sentry\Laravel\Integration;

try {
    $order = $client->trade()->placeOrder(...);
} catch (OKXException $e) {
    \Sentry\captureException($e);
    
    \Sentry\configureScope(function ($scope) use ($e) {
        $scope->setContext('okx', [
            'code' => $e->okxCode,
            'response' => $e->rawResponse
        ]);
    });
    
    throw $e;
}

Metrics

use Illuminate\Support\Facades\Cache;

class OKXMetrics
{
    public static function recordError(string $type): void
    {
        $key = "okx_errors:{$type}:" . now()->format('Y-m-d-H');
        Cache::increment($key, 1);
        Cache::expire($key, 3600 * 24); // 24 hours
    }
    
    public static function getErrorStats(string $type, int $hours = 24): int
    {
        $total = 0;
        
        for ($i = 0; $i < $hours; $i++) {
            $key = "okx_errors:{$type}:" . now()->subHours($i)->format('Y-m-d-H');
            $total += Cache::get($key, 0);
        }
        
        return $total;
    }
}

// Usage
try {
    $order = $client->trade()->placeOrder(...);
} catch (RateLimitException $e) {
    OKXMetrics::recordError('rate_limit');
    throw $e;
}

OKX Error Codes

Common codes

Code Description Exception
50111 Invalid API key AuthenticationException
50113 Invalid signature AuthenticationException
50011 Rate limit exceeded RateLimitException
51000–51099 Invalid parameters InvalidParameterException
54000–54099 Insufficient funds InsufficientFundsException
51008 Order amount too small InvalidParameterException
51020 Order price out of range InvalidParameterException

Full list: OKX Error Codes

Best Practices

  1. Always wrap API calls in try-catch
try {
    $result = $client->trade()->placeOrder(...);
} catch (OKXException $e) {
    // Handle the error
}
  1. Log errors with context
catch (OKXException $e) {
    Log::error('OKX Error', [
        'code' => $e->okxCode,
        'message' => $e->getMessage()
    ]);
}
  1. Retry on rate limit errors
catch (RateLimitException $e) {
    sleep(1);
    // Retry
}
  1. Validate before sending requests
if (!$validator->validateBalance(...)) {
    throw new \Exception('Insufficient balance');
}
  1. Track error frequency
OKXMetrics::recordError('rate_limit');

Back: ← Laravel Integration | Next: Examples →

Clone this wiki locally