Skip to content

Error Handling

This guide covers error response shapes, error codes, and retry strategies for the Synthetix API.

API Error Codes

Every order rejection now includes a machine-readable errorCode field alongside the human-readable error message. Use errorCode for programmatic handling; treat error as debug context only.

Response Shapes

Place / Cancel Orders

Trading rejections are per-item inside an HTTP 200 response:

{
  "status": "ok",
  "response": {
    "statuses": [
      {
        "resting": { "order": { "venueId": "123", "clientId": "0xabc" }, "id": "123" }
      },
      {
        "error": "reduce-only order requires open position",
        "errorCode": "REDUCE_ONLY_NO_POSITION",
        "order": { "venueId": "0", "clientId": "0xdef" }
      }
    ]
  },
  "timestamp": 1773351037815
}

Modify Order

Rejections are on the response object directly:

{
  "status": "ok",
  "response": {
    "order": { "venueId": "456", "clientId": "0xabc" },
    "orderId": "456",
    "status": "rejected",
    "error": "order 456 not found",
    "errorCode": "ORDER_NOT_FOUND",
    "timestamp": 1773351037815
  },
  "timestamp": 1773351037815
}

Request Validation Errors

Invalid requests return HTTP 400 with a top-level error (no errorCode per-item):

{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "category": "REQUEST",
    "message": "orders array cannot be empty",
    "retryable": false
  },
  "timestamp": 1773351037815
}

Trading Error Codes

Returned as per-item rejections with HTTP 200.

CodeDescriptionRetryable
CANCEL_FAILEDCancel operation failed at the matching engineNo
FOK_NOT_FILLEDFill-or-kill order could not be fully filled immediatelyNo
IDEMPOTENCY_CONFLICTDuplicate clientOrderIdNo
INSUFFICIENT_MARGINNot enough margin for the orderNo
INVALID_ORDER_SIDEInvalid order sideNo
INVALID_TRIGGER_PRICETrigger price invalid for order typeNo
IOC_NOT_FILLEDImmediate-or-cancel found no immediate executionNo
MARKET_CLOSEDMarket is not open for tradingNo
MARKET_NOT_FOUNDMarket does not existNo
MAX_ORDERS_EXCEEDEDOpen order limit reachedNo
MAX_SUB_ACCOUNTS_EXCEEDEDSubaccount limit reachedNo
NO_LIQUIDITYMarket order found no liquidity on the opposing sideNo
OI_CAP_EXCEEDEDOrder would exceed open interest capNo
OPERATION_TIMEOUTOperation timed outYes
ORDER_NOT_FOUNDOrder does not exist (cancel/modify)No
ORDER_REJECTED_BY_ENGINEMatching engine rejection with no specific code (see error for details)No
POSITION_NOT_FOUNDPosition does not existNo
POST_ONLY_WOULD_TRADEPost-only order would take liquidityNo
PRICE_OUT_OF_BOUNDSFill price exceeded configured cap/floorNo
QUANTITY_BELOW_FILLEDModified quantity is below already-filled amountNo
QUANTITY_TOO_SMALLQuantity below market minimumNo
REDUCE_ONLY_NO_POSITIONReduce-only order but no position existsNo
REDUCE_ONLY_SAME_SIDEReduce-only order on the same side as positionNo
REDUCE_ONLY_WOULD_INCREASEReduce-only order would increase position sizeNo
SELF_TRADE_PREVENTEDOrder would match against own resting orderNo
WICK_INSURANCE_ACTIVEWick protection period active, try again shortlyYes

Request / System Error Codes

Returned with HTTP 4xx/5xx as top-level errors.

CodeHTTPRetryable
VALIDATION_ERROR400No
MISSING_REQUIRED_FIELD400No
INVALID_FORMAT400No
INVALID_VALUE400No
UNAUTHORIZED401No
FORBIDDEN403No
NOT_FOUND404No
METHOD_NOT_ALLOWED405No
RATE_LIMIT_EXCEEDED429Yes
INTERNAL_ERROR500Yes
DATABASE_ERROR500Yes
CACHE_ERROR500Yes
UNKNOWN_ERROR500No

Retry Policy

Only three codes should be retried:

CodeStrategy
WICK_INSURANCE_ACTIVEWait a few seconds, retry unchanged
OPERATION_TIMEOUTRetry immediately or with short backoff
RATE_LIMIT_EXCEEDEDBackoff per rate limit headers

All other codes indicate a condition that won't resolve without changing the request or account state.

Batch Request Error Handling

Batch operations like placeOrders and cancelOrders return HTTP 200 with per-item statuses. Each item in the statuses array corresponds to the item at the same index in your request.

Key Points

  • Order preserved: Status array indices match the order of items in your request
  • Independent processing: Each item is validated independently
  • No rollback: Successfully processed items are not rolled back if others fail
  • Check every item: An HTTP 200 response does not mean all items succeeded

Handling Batch Responses

Always iterate through the statuses array and check for errorCode on each item:

function handleBatchResponse(response) {
  if (response.status === 'error') {
    // Request-level error - entire batch failed
    throw new Error(response.error.message);
  }
 
  const results = { succeeded: [], failed: [] };
 
  for (const status of response.response.statuses) {
    if (status.errorCode) {
      results.failed.push({ errorCode: status.errorCode, error: status.error });
    } else if (status.resting) {
      results.succeeded.push({ type: 'resting', id: status.resting.id });
    } else if (status.filled) {
      results.succeeded.push({ type: 'filled', ...status.filled });
    } else if (status.canceled) {
      results.succeeded.push({ type: 'canceled', id: status.canceled.id });
    }
  }
 
  return results;
}

WebSocket Error Handling

WebSocket connections use a different error format for responses:

{
  "id": "request-123",
  "requestId": "request-123",
  "status": 400,
  "timestamp": 1704067200000,
  "traceId": "abc123def456",
  "error": {
    "errorCode": "VALIDATION_ERROR",
    "code": 400,
    "message": "Invalid order parameters",
    "category": "REQUEST",
    "retryable": false,
    "details": {}
  }
}

WebSocket error fields:

  • errorCode - Specific error code for programmatic handling
  • category - Error category (REQUEST, AUTH, RATE_LIMIT, TRADING, SYSTEM)
  • retryable - Whether the operation can be retried
  • details - Additional context about the error

WebSocket-specific close codes:

  • Connection errors result in WebSocket close codes
  • Authentication failures close the connection with code 1008
  • Protocol errors use code 1002