Error Handling
The typed error-class hierarchy, error codes, narrowing patterns, and retry guidance for the Upshift SDK.
All public SDK methods throw typed errors that extend a common base class, AugustSDKError. Catch by class for broad handling, or narrow on the code field for actionable, per-failure logic.
Hierarchy
AugustSDKError // base — every SDK error extends this
├── AugustAuthError // missing / invalid / forbidden key
├── AugustValidationError // caller-side input failed validation
├── AugustNetworkError // network or transport-level failure
├── AugustTimeoutError // request exceeded its deadline
├── AugustRateLimitError // backend returned 429 (or equivalent)
└── AugustServerError // backend returned a non-OK statusEvery instance carries:
| Property | Type | Description |
|---|---|---|
code | AugustErrorCode | Stable string identifier — narrow on this for per-case handling. |
message | string | Human-readable description suitable for surfacing in dev tools. |
correlationId | string | undefined | Set from the response x-correlation-id header when the SDK received one. |
context | Record<string, unknown>? | Structured payload (e.g. { target, amount }) attached for logging. |
cause | unknown | Original error from the underlying RPC / fetch — preserved for inspection. |
stack | string | Native stack trace. |
AugustTimeoutError adds timeoutMs (the deadline that elapsed). AugustRateLimitError adds an optional retryAfterMs. AugustServerError adds status (the HTTP status code).
All instances expose toJSON() so JSON.stringify(err) returns a useful object — native Error serializes to "{}" without this.
Codes
| Class | Code | When it fires | Recommended action |
|---|---|---|---|
AugustAuthError | AUTH_MISSING_KEY | Required Upshift API key was not supplied to the SDK constructor. | Pass keys.august into new AugustSDK({...}). |
AugustAuthError | AUTH_INVALID_KEY | Backend rejected the supplied key (malformed or revoked). | Verify the key value; rotate if needed. |
AugustAuthError | AUTH_FORBIDDEN | Backend returned HTTP 403 — key lacks permission for this endpoint. | Check the key's permitted scopes; do not retry. |
AugustAuthError | AUTH_UNAUTHORIZED | Backend returned HTTP 401 — key was not accepted. | Re-verify the key; do not retry without rotating. |
AugustValidationError | INVALID_INPUT | Required field missing, NaN / Infinity amount, or JS number above Number.MAX_SAFE_INTEGER. | Fix the input before retrying. |
AugustValidationError | INVALID_ADDRESS | Address fails checksum / wrong length, or is not valid for the target chain family (EVM / Solana). | Fix the input. |
AugustValidationError | INVALID_CHAIN | Chain ID is not in the SDK's allowlist for this operation. | Pass a supported chain. |
AugustValidationError | INVALID_URL | A user-supplied URL failed validation. | Fix the URL. |
AugustValidationError | ACCOUNT_NOT_FUNDED | Stellar (Soroban) deposit/redeem source account has never been activated on-chain (zero balance). | Send ≥1 XLM to the address to activate it, then retry. |
AugustNetworkError | NETWORK_ERROR | Underlying fetch / WebSocket failed (DNS, connection refused, offline). | Safe to retry with backoff. |
AugustTimeoutError | TIMEOUT | Request exceeded its deadline (default 90 s, configurable per-call). | Backoff + retry; check chain / RPC status. |
AugustRateLimitError | RATE_LIMITED | Backend returned HTTP 429 (or equivalent rate-limit signal). | Wait retryAfterMs before retrying. |
AugustServerError | SERVER_ERROR | Backend returned a non-OK status that is not auth / rate-limit / timeout. | Inspect .status and .context; retry conservatively. |
AugustSDKError (base) | UNKNOWN | Wrapped failure from an RPC / contract call. Original error preserved on .cause. | Inspect .cause and .context; do not retry blindly. |
Narrowing patterns
Catch broadly, narrow by code
Best for transactional flows where you want one block of recovery logic per failure category.
import {
AugustAuthError,
AugustValidationError,
AugustTimeoutError,
AugustRateLimitError,
AugustSDKError,
} from '@augustdigital/sdk';
try {
await augustSdk.evm.vaultDeposit({
target: vaultAddress,
wallet: userAddress,
amount: '100',
});
} catch (err) {
if (err instanceof AugustValidationError) {
if (err.code === 'INVALID_INPUT') return showFormError(err.message);
if (err.code === 'INVALID_ADDRESS') return showAddressError();
if (err.code === 'ACCOUNT_NOT_FUNDED') {
// Stellar only: the source account needs ≥1 XLM to be activated on-chain.
// err.message includes the address and the funding hint.
return showFundingPrompt(err.message);
}
}
if (err instanceof AugustAuthError) {
return showAuthError('Check your August API key.');
}
if (err instanceof AugustTimeoutError) {
// err.timeoutMs is the deadline that elapsed
return scheduleRetry(err.timeoutMs);
}
if (err instanceof AugustRateLimitError) {
return scheduleRetry(err.retryAfterMs ?? 5_000);
}
if (err instanceof AugustSDKError) {
// Wrapped downstream failure — inspect cause for the underlying revert / RPC error
reportError(err.code, err.context, err.cause);
return;
}
throw err;
}Catch using the type guard
Use isAugustSDKError when you receive an unknown value (e.g. in a global error handler) and want to keep the rest of the runtime's errors flowing through untouched.
import { isAugustSDKError } from '@augustdigital/sdk';
window.addEventListener('unhandledrejection', (event) => {
if (isAugustSDKError(event.reason)) {
reportSdkError(event.reason);
event.preventDefault();
}
});The guard works across realms (Web Worker, VM contexts) where instanceof fails.
Inspect the original cause
Wrapped errors keep the underlying failure on .cause:
try {
await augustSdk.evm.vaultRequestRedeem({ target, wallet, amount });
} catch (err) {
if (err instanceof AugustSDKError && err.code === 'UNKNOWN') {
console.error('Underlying error:', err.cause);
console.error('Context:', err.context);
}
throw err;
}For on-chain reverts, .cause is the original ethers / viem error, so revert reasons and gas info remain accessible.
Retry guidance
| Class | Safe to retry? | Notes |
|---|---|---|
AugustValidationError | No | The input is wrong; retrying will fail the same way. |
AugustAuthError | No | Fix the key first. |
AugustNetworkError | Yes, with backoff | Transient by definition. |
AugustTimeoutError | Yes, with backoff | Consider raising timeoutMs if the operation is legitimately slow. |
AugustRateLimitError | Yes, after retryAfterMs | Honor the suggested wait. |
AugustServerError | Conditionally | Retry on 5xx with backoff; treat 4xx as terminal. |
AugustSDKError UNKNOWN | Conditionally | Inspect .cause first — an on-chain revert is not retryable. |
Serialization for logging
Every SDK error implements toJSON(), so logging with JSON.stringify preserves the structured fields:
try {
await augustSdk.getVault({ vault });
} catch (err) {
if (err instanceof AugustSDKError) {
logger.error('sdk_error', JSON.stringify(err));
}
throw err;
}The serialized payload includes name, message, code, correlationId, context, stack, and a compact cause summary.