.NET

Error Handling

Exception hierarchy and error handling in the .NET SDK

The SDK uses a hierarchical exception system, all inheriting from SsiException (and System.Exception).

Exception List

ExceptionDerives fromDescription
SsiExceptionExceptionBase exception. Has Code, StatusCode, ResponseBody, Headers
AuthenticationExceptionSsiExceptionAuthentication failed (bad credentials, expired token, signature error)
SmartOtpPendingExceptionAuthenticationExceptionSmart OTP not approved yet (HTTP 202, code 401114), or polling ran out without approval
SmartOtpRejectedExceptionAuthenticationExceptionSmart OTP rejected or invalid (401113, 401115, 401116). Terminal, no more polling.
ReauthenticationRequiredExceptionAuthenticationExceptionThe token is expired, cannot be refreshed and no new otp/transactionId was given: authenticate again
ApiExceptionSsiExceptionAPI returned an error. Has Code, StatusCode, ResponseBody
DuplicateRequestExceptionApiExceptionDuplicate request (HTTP 409, default code "409")
WebSocketExceptionSsiExceptionWebSocket connection or communication error
TradingWsErrorWebSocketExceptionTrading WebSocket: the server rejected the socket or a command. Has Status, ErrorCode, ServerMessage
ValidationExceptionSsiExceptionInput validation error, detected on the client, nothing sent
RateLimitExceptionSsiExceptionRequest rate limit exceeded (HTTP 429). Default code "RATE_LIMITED"

Inheritance Chain

Exception → SsiException → AuthenticationException → SmartOtpPendingException
                                                   → SmartOtpRejectedException
                                                   → ReauthenticationRequiredException
                         → ApiException → DuplicateRequestException
                         → WebSocketException → TradingWsError
                         → ValidationException
                         → RateLimitException

Base exception properties

public class SsiException : Exception
{
    public string Code { get; }                              // server error code (e.g. "401114"); otherwise the HTTP status or an SDK code
    public int StatusCode { get; }
    public Dictionary<string, JsonElement>? ResponseBody { get; }   // body with secrets masked
    public IReadOnlyDictionary<string, string> Headers { get; }     // response headers (rate limit, Retry-After...), never Authorization
}

RateLimitException adds:

public long? RetryAfterMs { get; }   // milliseconds the server asked to wait (Retry-After), when sent
public int? Limit { get; }
public int? Remaining { get; }
public double? Reset { get; }

TradingWsError adds int Status, string ErrorCode, string ServerMessage. Its message never contains signed params.

Server error codes: ServerErrorCode and HttpStatus

ServerErrorCode is a static class holding the numeric codes the server returns in the code field (for example PushApprovalPending = 401114, SignatureMissing = 401006, SignatureBodyEmpty = 401008, SignatureMismatch = 401013, ReplayWindowExceeded = 400111, OtpVerificationFailed = 401100, NoScopeGranted = 403100), plus ServerErrorCode.IsKnown(int code). For the full list see the SDK source and Error Codes. HttpStatus has the classification helpers IsAuthError, IsRateLimit, IsClientError, IsServerError.

Basic usage

using SsiSdk;

try
{
    await auth.AuthenticateAsync("YOUR_OTP");
}
catch (SsiException ex)
{
    Console.WriteLine($"Error: {ex.Message} (code: {ex.Code})");
}

Handling by error type

Controls applied: the account is a placeholder, catch blocks go from specific to general, no automatic retry for orders (the SDK never resends POST), and neither ResponseBody nor tokens are printed to logs.

using SsiSdk;

try
{
    var result = await trading.Trading.PlaceLimitOrderAsync(
        "YOUR_ACCOUNT_NO", "SSI", OrderSide.Buy, 100, 68000m);
}
catch (SmartOtpRejectedException)
{
    Console.WriteLine("Smart OTP was rejected, request a new OTP");
}
catch (SmartOtpPendingException)
{
    Console.WriteLine("Smart OTP not approved yet");
}
catch (ReauthenticationRequiredException)
{
    Console.WriteLine("Token expired, authenticate again");
}
catch (AuthenticationException)
{
    Console.WriteLine("Re-authentication required");
}
catch (RateLimitException ex)
{
    Console.WriteLine($"Rate limited, retry after {ex.RetryAfterMs} ms");
}
catch (DuplicateRequestException ex)
{
    // Must be caught before ApiException
    Console.WriteLine($"Duplicate request (409): {ex.Message}");
}
catch (ApiException ex)
{
    Console.WriteLine($"API error {ex.StatusCode}: {ex.Message}");
}
catch (ValidationException ex)
{
    Console.WriteLine($"Validation: {ex.Message}");
}
catch (TradingWsError ex)
{
    Console.WriteLine($"Trading WS rejected: {ex.Status} {ex.ErrorCode}");
}
catch (WebSocketException ex)
{
    Console.WriteLine($"WebSocket: {ex.Message}");
}
catch (SsiException ex)
{
    Console.WriteLine($"SDK error: {ex.Message}");
}

The example above mixes REST and WebSocket errors to show the catch order; in practice a single call throws only some of them (TradingWsError comes only from the Trading WebSocket).

Handling tips

  • Catch specific exceptions first and SsiException last as a fallback (order catch blocks from specific to general). SmartOtp*/ReauthenticationRequiredException must come before AuthenticationException, DuplicateRequestException before ApiException, TradingWsError before WebSocketException.
  • Catch AuthenticationException when you need to refresh the token or request a new OTP. With Smart OTP, SmartOtpPendingException means not approved yet (you may try again), while SmartOtpRejectedException is terminal: ask for a new OTP.
  • Catch RateLimitException and respect RetryAfterMs (milliseconds) to back off. The SDK waits out a 429 only for GET; POST/PUT/DELETE are never resent.
  • Catch ApiException to handle business errors from the server (Code, StatusCode, ResponseBody).
  • Catch WebSocketException when handling streaming. After a command timeout the outcome is unknown: check the order book before resending.
  • Catch ValidationException for invalid input before calling the API (the SDK validates on the client; no request is sent).

On this page