Skip to main content
Every public service method throws a typed exception for the error conditions you care about. Network blips are auto-retried via Polly before surfacing.

Exceptions you’ll see

ExceptionWhenRecovery
STXWrongCredentialsExceptionEmail or password rejected at loginPrompt user for fresh creds
STXSessionExpiredExceptionJWT and refresh token both expiredCall STXLoginService.LoginAsync again
STXTokenExpiredExceptionJWT expired but refresh token is still validCall STXTokenService.RefreshTokenAsync, or rely on keepSessionAlive: true
STXCancelOnDisconnectNotEnabledExceptionConfirmOrderAsync(cancelOnDisconnect: true) called before joining STXActiveOrdersChannelJoin the channel first, or pass cancelOnDisconnect: false
STXGeoComplyExceptionGeo-compliance check failedUser is outside a permitted jurisdiction — surface to your UI
GraphQLHttpRequestExceptionNon-200 HTTP response from the GraphQL endpointUsually transient; retries handled internally (see below)
All STX-prefixed exceptions live in STX.Sdk.Exceptions.

Example: handle auth failures

try
{
    await login.LoginAsync(email, password);
}
catch (STXWrongCredentialsException)
{
    // User-facing: "Email or password is incorrect."
}
catch (STXGeoComplyException ex)
{
    // User-facing: "Trading isn't available in your region."
    _logger.LogWarning(ex, "Geo block");
}

Example: refresh on expired token

When not using keepSessionAlive: true, wrap trading calls with a token-refresh retry:
async Task<T> WithSession<T>(Func<Task<T>> call)
{
    try { return await call(); }
    catch (STXTokenExpiredException)
    {
        await _tokens.RefreshTokenAsync();
        return await call();
    }
    catch (STXSessionExpiredException)
    {
        await _login.LoginAsync(_email, _password);
        return await call();
    }
}

var order = await WithSession(() =>
    _orders.ConfirmOrderAsync(price, qty, marketId, action, type));

Transient retries (automatic)

Every GraphQL mutation runs through STXGraphQLBaseService.SendMutationWithRetry, which uses Polly to retry on:
  • HttpRequestException
  • TaskCanceledException (timeout)
  • 5xx responses from the GraphQL endpoint
Default policy: 3 retries with exponential backoff (200ms → 800ms). You don’t need to add your own retry around SDK calls — double-retry actually hurts because it amplifies brief outages. If you hit a 5xx repeatedly after the built-in retries, the underlying GraphQLHttpRequestException surfaces. Treat it as a service outage, not a programmer error — back off and try again later.

Rate limits

The exchange rate-limits by IP + account. Bursty order placement can return an HTTP 429. The SDK does not retry 429s automatically (retrying would make it worse). If you see them, lower your request rate or batch via ConfirmOrdersAsync.

Debugging

Enable Debug logging to see full GraphQL request/response bodies:
services.AddLogging(builder =>
    builder.SetMinimumLevel(LogLevel.Debug).AddConsole());
Every call is logged with its operation name, variables, and elapsed time. That’s usually enough to tell whether an error was transient or a schema mismatch.