UpshiftDocs

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 status

Every instance carries:

PropertyTypeDescription
codeAugustErrorCodeStable string identifier — narrow on this for per-case handling.
messagestringHuman-readable description suitable for surfacing in dev tools.
correlationIdstring | undefinedSet from the response x-correlation-id header when the SDK received one.
contextRecord<string, unknown>?Structured payload (e.g. { target, amount }) attached for logging.
causeunknownOriginal error from the underlying RPC / fetch — preserved for inspection.
stackstringNative 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

ClassCodeWhen it firesRecommended action
AugustAuthErrorAUTH_MISSING_KEYRequired Upshift API key was not supplied to the SDK constructor.Pass keys.august into new AugustSDK({...}).
AugustAuthErrorAUTH_INVALID_KEYBackend rejected the supplied key (malformed or revoked).Verify the key value; rotate if needed.
AugustAuthErrorAUTH_FORBIDDENBackend returned HTTP 403 — key lacks permission for this endpoint.Check the key's permitted scopes; do not retry.
AugustAuthErrorAUTH_UNAUTHORIZEDBackend returned HTTP 401 — key was not accepted.Re-verify the key; do not retry without rotating.
AugustValidationErrorINVALID_INPUTRequired field missing, NaN / Infinity amount, or JS number above Number.MAX_SAFE_INTEGER.Fix the input before retrying.
AugustValidationErrorINVALID_ADDRESSAddress fails checksum / wrong length, or is not valid for the target chain family (EVM / Solana).Fix the input.
AugustValidationErrorINVALID_CHAINChain ID is not in the SDK's allowlist for this operation.Pass a supported chain.
AugustValidationErrorINVALID_URLA user-supplied URL failed validation.Fix the URL.
AugustValidationErrorACCOUNT_NOT_FUNDEDStellar (Soroban) deposit/redeem source account has never been activated on-chain (zero balance).Send ≥1 XLM to the address to activate it, then retry.
AugustNetworkErrorNETWORK_ERRORUnderlying fetch / WebSocket failed (DNS, connection refused, offline).Safe to retry with backoff.
AugustTimeoutErrorTIMEOUTRequest exceeded its deadline (default 90 s, configurable per-call).Backoff + retry; check chain / RPC status.
AugustRateLimitErrorRATE_LIMITEDBackend returned HTTP 429 (or equivalent rate-limit signal).Wait retryAfterMs before retrying.
AugustServerErrorSERVER_ERRORBackend returned a non-OK status that is not auth / rate-limit / timeout.Inspect .status and .context; retry conservatively.
AugustSDKError (base)UNKNOWNWrapped 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

ClassSafe to retry?Notes
AugustValidationErrorNoThe input is wrong; retrying will fail the same way.
AugustAuthErrorNoFix the key first.
AugustNetworkErrorYes, with backoffTransient by definition.
AugustTimeoutErrorYes, with backoffConsider raising timeoutMs if the operation is legitimately slow.
AugustRateLimitErrorYes, after retryAfterMsHonor the suggested wait.
AugustServerErrorConditionallyRetry on 5xx with backoff; treat 4xx as terminal.
AugustSDKError UNKNOWNConditionallyInspect .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.