STX.Sdk throws typed exceptions for every error condition you need to handle in application code. Network blips and transient HTTP failures are retried automatically via Polly before an exception ever reaches your code, so the exceptions you catch represent genuine problems that require action.
Exceptions
All STX-prefixed exceptions live in the STX.Sdk.Exceptions namespace.
| Exception | When thrown | Recommended action |
|---|
STXWrongCredentialsException | Email or password rejected at login | Prompt the user for fresh credentials |
STXTokenExpiredException | JWT expired; refresh token still valid | Call STXTokenService.RefreshTokenAsync |
STXSessionExpiredException | Both JWT and refresh token expired | Call STXLoginService.LoginAsync again |
STXCancelOnDisconnectNotEnabledException | ConfirmOrderAsync(cancelOnDisconnect: true) called before joining STXActiveOrdersChannel | Join the channel first, or set cancelOnDisconnect: false |
STXGeoComplyException | Geo-compliance check failed | User is outside a permitted jurisdiction — surface to your UI |
GraphQLHttpRequestException | Non-200 HTTP response after all retries exhausted | Treat as a service outage; back off and retry later |
Handle authentication failures
try
{
await login.LoginAsync(email, password);
}
catch (STXWrongCredentialsException)
{
// Surface to UI: "Email or password is incorrect."
}
catch (STXGeoComplyException ex)
{
// Surface to UI: "Trading isn't available in your region."
_logger.LogWarning(ex, "Geo block on login");
}
Handle session expiry in trading calls
If you are not using keepSessionAlive: true, wrap trading calls with a token-refresh retry to handle expiry gracefully:
async Task<T> WithSession<T>(Func<Task<T>> call)
{
try
{
return await call();
}
catch (STXTokenExpiredException)
{
// Refresh token is still valid — exchange it for a new JWT
await _tokens.RefreshTokenAsync();
return await call();
}
catch (STXSessionExpiredException)
{
// Both tokens expired — full re-login required
await _login.LoginAsync(_email, _password);
return await call();
}
}
var order = await WithSession(() =>
_orders.ConfirmOrderAsync(price, qty, marketId, action, orderType));
Using keepSessionAlive: true at login eliminates the need for this wrapper in long-running bots. STXSessionBackgroundService handles refresh automatically in the background.
Automatic transient retries
Every GraphQL mutation runs through STXGraphQLBaseService.SendMutationWithRetry, which uses Polly with the following default policy:
- Retry triggers:
HttpRequestException, TaskCanceledException (timeout), and 5xx responses.
- Attempts: 3 retries after the initial failure (4 total).
- Backoff: exponential — 200 ms, 400 ms, 800 ms.
You do not need to add your own retry around SDK calls. Doing so can amplify brief outages by turning a single transient failure into multiple redundant requests.
If the error persists after all built-in retries, a GraphQLHttpRequestException surfaces. Treat it as a service outage — back off and try again after a delay.
Rate limits
The exchange rate-limits by IP and account. Bursty order placement can produce HTTP 429 responses.
The SDK does not retry 429 responses automatically. Retrying a 429 makes the situation worse. If you see them, reduce your request rate or consolidate orders with ConfirmOrdersAsync.
Debug logging
Enable Debug-level logging to inspect full GraphQL request and response bodies, including operation names, variables, and elapsed time. This is usually enough to determine whether an error is a transient network issue or a schema mismatch:
services.AddLogging(builder =>
builder
.SetMinimumLevel(LogLevel.Debug)
.AddConsole());
See also