> This page is for Taurus PRIME.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://taurushq.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://taurushq.ferndocs.com/_mcp/server.

# Error Handling

# Error Handling

This guide covers the error response format and error codes used by the PRIME API.

## Error Response Format

All errors return a JSON object with the following structure:

```json
{
  "code": 400,
  "message": "Invalid quantity: must be positive",
  "status": "INVALID_ARGUMENT",
  "details": []
}
```

### Fields

| Field     | Type    | Description                         |
| --------- | ------- | ----------------------------------- |
| `code`    | integer | HTTP status code                    |
| `message` | string  | Human-readable error description    |
| `status`  | string  | gRPC status code name               |
| `details` | array   | Additional error context (optional) |

---

## HTTP Status Codes

### Client Errors (4xx)

| Code | Status                | Description                       |
| ---- | --------------------- | --------------------------------- |
| 400  | `INVALID_ARGUMENT`    | Request validation failed         |
| 401  | `UNAUTHENTICATED`     | Authentication required or failed |
| 403  | `PERMISSION_DENIED`   | Authenticated but not authorized  |
| 404  | `NOT_FOUND`           | Resource does not exist           |
| 409  | `ALREADY_EXISTS`      | Resource already exists           |
| 422  | `FAILED_PRECONDITION` | Business rule violation           |
| 429  | `RESOURCE_EXHAUSTED`  | Rate limit exceeded               |

### Server Errors (5xx)

| Code | Status        | Description                     |
| ---- | ------------- | ------------------------------- |
| 500  | `INTERNAL`    | Unexpected server error         |
| 503  | `UNAVAILABLE` | Service temporarily unavailable |

---

## Error Reasons

The API uses specific reason codes to indicate the type of error.

### Authentication & Authorization

| Reason                 | Description                            |
| ---------------------- | -------------------------------------- |
| `UNAUTHENTICATED`      | Missing or invalid authentication      |
| `MFA_REQUIRED`         | Multi-factor authentication needed     |
| `PERMISSION_DENIED`    | Insufficient permissions for operation |
| `ACCESS_REFUSED`       | Access explicitly denied               |
| `ACCOUNT_IS_SUSPENDED` | Account has been suspended             |

### Validation Errors

| Reason                      | Description                         |
| --------------------------- | ----------------------------------- |
| `INVALID_ARGUMENTS`         | Request parameters invalid          |
| `INCORRECT_PARAMETER_VALUE` | Specific parameter value invalid    |
| `WEAK_PASSWORD`             | Password does not meet requirements |
| `USERNAME_ALREADY_EXISTS`   | Username is taken                   |

### Resource Errors

| Reason                | Description                       |
| --------------------- | --------------------------------- |
| `RESOURCE_NOT_FOUND`  | Requested resource does not exist |
| `ALREADY_EXISTS`      | Resource already exists           |
| `CONSTRAINT_IN_PLACE` | Operation blocked by constraint   |

### Trading Errors

| Reason                 | Description                         |
| ---------------------- | ----------------------------------- |
| `NOT_ENOUGH_FUNDS`     | Insufficient balance for operation  |
| `CANNOT_RESOLVE_PRICE` | Unable to determine execution price |
| `CLIP_SIZE_TOO_SMALL`  | Order quantity below minimum        |
| `CLIP_SIZE_TOO_LARGE`  | Order quantity above maximum        |

### Deposit/Withdrawal Errors

| Reason                        | Description                         |
| ----------------------------- | ----------------------------------- |
| `DEPOSIT_NOT_ENABLED`         | Deposits disabled for instrument    |
| `DEPOSIT_ALREADY_IN_PROGRESS` | Duplicate deposit detected          |
| `DEPOSIT_ALREADY_CANCELED`    | Deposit was already cancelled       |
| `DEPOSIT_CANNOT_BE_CANCELED`  | Deposit cannot be cancelled         |
| `WITHDRAWAL_NOT_ENABLED`      | Withdrawals disabled for instrument |

### System Errors

| Reason             | Description                     |
| ------------------ | ------------------------------- |
| `INTERNAL`         | Internal server error           |
| `UNAVAILABLE`      | Service temporarily unavailable |
| `SERVER_EXHAUSTED` | Server resources exhausted      |
| `CAPTCHA_FAILED`   | CAPTCHA verification failed     |

---

## Resource Names

Errors may include a resource name indicating which entity caused the error.

| Resource         | Description            |
| ---------------- | ---------------------- |
| `USER`           | User account           |
| `CLIENT_ACCOUNT` | Client account entity  |
| `SUBACCOUNT`     | Sub-account/portfolio  |
| `INSTRUMENT`     | Tradable asset         |
| `PAIR`           | Trading pair           |
| `ORDER`          | Trading order          |
| `TRADE`          | Executed trade         |
| `BALANCE`        | Account balance        |
| `DEPOSIT`        | Deposit transaction    |
| `WITHDRAWAL`     | Withdrawal transaction |
| `TRANSFER`       | Internal transfer      |
| `BANK_ACCOUNT`   | Bank account           |
| `ADDRESS`        | Crypto address         |
| `DOCUMENT`       | KYC document           |
| `FEE`            | Fee calculation        |
| `HOLDING`        | Asset holding          |
| `LISTING`        | OTC listing            |
| `EXPOSURE_LINE`  | Risk exposure          |

---

## Error Examples

### Invalid Request

```json
{
  "code": 400,
  "message": "quantity: must be greater than 0",
  "status": "INVALID_ARGUMENT",
  "details": []
}
```

### Authentication Failed

```json
{
  "code": 401,
  "message": "JWT token was invalid",
  "status": "UNAUTHENTICATED",
  "details": []
}
```

### MFA Required

When MFA is enabled for a user but the TOTP code (`challenge` field) was not provided in the login request:

```json
{
  "code": 16,
  "message": "MFA challenge required",
  "details": [{
    "@type": "type.googleapis.com/tgtraded.Error",
    "reason": "MFA_REQUIRED"
  }]
}
```

To resolve, re-submit the login request with the `challenge` field containing the TOTP code.

### Permission Denied

```json
{
  "code": 403,
  "message": "User does not have trade permission on sub-account",
  "status": "PERMISSION_DENIED",
  "details": []
}
```

### Resource Not Found

```json
{
  "code": 404,
  "message": "Order not found",
  "status": "NOT_FOUND",
  "details": [{
    "resource": "ORDER",
    "id": "order-uuid"
  }]
}
```

### Insufficient Funds

```json
{
  "code": 403,
  "message": "Insufficient CHF balance: required 50000.00, available 25000.00",
  "status": "FAILED_PRECONDITION",
  "details": [{
    "reason": "NOT_ENOUGH_FUNDS",
    "resource": "BALANCE",
    "required": "50000.00",
    "available": "25000.00"
  }]
}
```

### Rate Limited

```json
{
  "code": 429,
  "message": "Rate limit exceeded. Retry after 60 seconds",
  "status": "RESOURCE_EXHAUSTED",
  "details": [{
    "reason": "SERVER_EXHAUSTED",
    "retry_after": 60
  }]
}
```

---

## Handling Errors

### Best Practices

1. **Check HTTP status code first** - Categorize by 4xx vs 5xx
2. **Parse the error response** - Extract `message` and `details`
3. **Handle specific reasons** - Implement logic for expected errors
4. **Log unexpected errors** - Include correlation ID for support
5. **Implement retries for 5xx** - Use exponential backoff

### Example Error Handler (JavaScript)

```javascript
async function apiRequest(url, options) {
  const response = await fetch(url, options);

  if (!response.ok) {
    const error = await response.json();

    switch (error.status) {
      case 'UNAUTHENTICATED':
        // Refresh token or redirect to login
        if (error.details?.some(d => d.reason === 'MFA_REQUIRED')) {
          return handleMfaChallenge(error.details);
        }
        return redirectToLogin();

      case 'PERMISSION_DENIED':
        throw new Error('You do not have permission for this action');

      case 'NOT_FOUND':
        throw new Error('Resource not found');

      case 'RESOURCE_EXHAUSTED':
        // Implement retry with backoff
        const retryAfter = error.details?.[0]?.retry_after || 60;
        await sleep(retryAfter * 1000);
        return apiRequest(url, options);

      default:
        throw new Error(error.message || 'An error occurred');
    }
  }

  return response.json();
}
```

### Retry Strategy

For transient errors (5xx, rate limits):

```javascript
async function withRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      if (error.status >= 500 || error.status === 429) {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await sleep(delay);
      } else {
        throw error; // Don't retry client errors
      }
    }
  }
}
```

---

## Correlation ID

Every request is assigned a correlation ID for tracing. Include this ID when contacting support.

### Finding the Correlation ID

1. Check response headers: `X-Correlation-ID`
2. Check error details: may include `correlation_id` field

### Providing Your Own

You can provide a correlation ID in the request:

```bash
curl -X GET "https://api.t-dx.com/api/rest/v1/orders" \
  -H "Authorization: Bearer <token>" \
  -H "X-Correlation-ID: your-uuid-here"
```