> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qfex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

All websocket channels may return an **error frame** instead of the expected response. Error frames share a single, consistent schema.

## Error Envelope

```json theme={null}
{
  "err": {
    "error_code": "RateLimited",
    "message": "You have exceeded the allowed message rate.",
    "incoming_message": {
      "type": "add_order",
      "params": {
        "symbol": "AAPL",
        "side": "BUY",
        "order_type": "LIMIT",
        "order_time_in_force": "GTC",
        "quantity": 10.0,
        "price": 200.0,
        "client_order_id": "my-order-001",
        "take_profit": null,
        "stop_loss": null
      }
    }
  }
}
```

### Fields

| Field              | Type        | Required | Description                                                                        |
| ------------------ | ----------- | -------- | ---------------------------------------------------------------------------------- |
| `error_code`       | string      | Yes      | Machine-readable error code (see list below). Matches server enum variant exactly. |
| `message`          | string/null | No       | Human-readable context. May be `null`.                                             |
| `incoming_message` | object/null | No       | Echo of the request that caused the error (your original message), when available. |

> Note: `incoming_message` uses the same tagged format as requests (`{ "type": "<snake_case>", "params": { ... } }`).

## Error Codes

These values map 1:1 to the server enum:

| Code                             | When it happens                                                                         | Suggested Client Action                                                                                  |
| -------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `RateLimited`                    | You’ve exceeded the allowed message rate (weighted per message type).                   | Backoff and retry later. Consider batching and respecting published limits.                              |
| `InvalidJSONFormat`              | Message is not valid JSON or doesn’t match the expected schema.                         | Validate JSON and schema before sending. Include required fields and correct types/enum values.          |
| `AlreadyAuthenticated`           | You attempted to authenticate again after a successful auth on the same connection.     | Do not re-auth on an already authenticated socket.                                                       |
| `InvalidParameter`               | One or more parameters are out of range, missing, or invalid for the specified message. | Correct the parameter(s) and retry.                                                                      |
| `PermissionDenied`               | The API key or user lacks permission for the requested action.                          | Check key scopes/roles; contact support if needed.                                                       |
| `ServerError`                    | Unexpected internal error.                                                              | Safe to retry after a short delay; if persistent, contact support with timestamp and `incoming_message`. |
| `InvalidOrder`                   | The order is invalid or malformed.                                                      | Check order parameters and format; correct the order and retry.                                          |
| `AlreadySubscribedToLeaderboard` | The user is already subscribed to a leaderboard.                                        | Do not attempt to subscribe again; check current subscriptions first.                                    |
| `TncRequired`                    | The user needs to agree to the latest Terms and Conditions.                             | Agree to the latest Terms and Conditions.                                                                |

### Rate limiting notes

The server computes **message weight** by the message `"type"` (e.g., `"add_order"`, `"cancel_order"`) and applies limits over a sliding window. Some messages are heavier than others. If you see `RateLimited`, delay and reduce frequency; prefer fewer, larger requests when possible.

## Examples

### 1) Invalid JSON

```json theme={null}
{
  "err": {
    "error_code": "InvalidJSONFormat",
    "message": "Expected field `symbol` (string).",
    "incoming_message": {
      "type": "add_order",
      "params": {
        "side": "BUY",
        "order_type": "LIMIT",
        "order_time_in_force": "GTC",
        "quantity": 10.0,
        "price": 200.0
      }
    }
  }
}
```

### 2) Already authenticated

```json theme={null}
{
  "err": {
    "error_code": "AlreadyAuthenticated",
    "message": "Connection is already authenticated.",
    "incoming_message": { "type": "auth", "params": { ... } }
  }
}
```

### 3) Invalid parameter

```json theme={null}
{
  "err": {
    "error_code": "InvalidParameter",
    "message": "price must be >= min_tick and within price bands.",
    "incoming_message": {
      "type": "modify_order",
      "params": {
        "symbol": "AAPL",
        "order_id": "cd1a2b0c-...-f3",
        "quantity": 10.0,
        "price": 0.0001,
        "take_profit": 0.0,
        "stop_loss": 0.0,
        "side": "BUY"
      }
    }
  }
}
```

### 4) Permission denied

```json theme={null}
{
  "err": {
    "error_code": "PermissionDenied",
    "message": "API key does not allow trading on this account.",
    "incoming_message": {
      "type": "transfer_balance",
      "params": { "amount": 100.0, "to_api_key": "****" }
    }
  }
}
```

### 5) Rate limited

```json theme={null}
{
  "err": {
    "error_code": "RateLimited",
    "message": "Too many requests. Please retry after 200ms.",
    "incoming_message": { "type": "cancel_all_orders", "params": {} }
  }
}
```

### 6) Server error

```json theme={null}
{
  "err": {
    "error_code": "ServerError",
    "message": "Unexpected error processing request.",
    "incoming_message": { "type": "get_user_trades", "params": { "limit": 50 } }
  }
}
```

## Client Handling Patterns

<AccordionGroup>
  <Accordion icon="python" title="Python">
    ```python theme={null}
    import json
    import websocket

    def on_message(ws, message):
        msg = json.loads(message)
        if msg.get("type") == "error":
            code = msg.get("error_code")
            print(f"[ERROR] {code}: {msg.get('message')}")
            # basic strategy
            if code == "RateLimited":
                # implement backoff before retrying your last request
                return
            elif code in ("InvalidJSONFormat", "InvalidParameter"):
                # fix your payload; don't auto-retry
                return
            elif code in ("PermissionDenied", "AlreadyAuthenticated"):
                # adjust flow; no retry
                return
            else:
                # ServerError or unknown: small backoff and optional retry
                return
        else:
            print("OK:", msg)
    ```
  </Accordion>

  <Accordion icon="node" title="Node">
    ```javascript theme={null}
    import WebSocket from "ws";

    const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY");

    ws.on("message", (raw) => {
      const msg = JSON.parse(raw.toString());
      if (msg.type === "error") {
        const { error_code, message } = msg;
        console.error(`[ERROR] ${error_code}: ${message || ""}`);

        switch (error_code) {
          case "RateLimited":
            // schedule retry w/ backoff
            break;
          case "InvalidJSONFormat":
          case "InvalidParameter":
            // fix payload, do not blind-retry
            break;
          case "PermissionDenied":
          case "AlreadyAuthenticated":
            // update client state/flow
            break;
          default:
            // transient server error: optional retry with jitter
            break;
        }
        return;
      }

      // handle normal messages…
    });
    ```
  </Accordion>

  <Accordion icon="golang" title="Go">
    ```go theme={null}
    type ErrorFrame struct {
        Type            string          `json:"type"`
        ErrorCode       string          `json:"error_code"`
        Message         *string         `json:"message,omitempty"`
        IncomingMessage json.RawMessage `json:"incoming_message,omitempty"`
    }

    func handleMessage(raw []byte) {
        // peek at "type"
        var meta struct{ Type string `json:"type"` }
        if err := json.Unmarshal(raw, &meta); err != nil { return }

        if meta.Type == "error" {
            var ef ErrorFrame
            if err := json.Unmarshal(raw, &ef); err == nil {
                log.Printf("[ERROR] %s: %s", ef.ErrorCode, deref(ef.Message))
                // handle per code...
            }
            return
        }

        // normal flow...
    }
    ```
  </Accordion>
</AccordionGroup>

## Authentication & Errors

* **trade.qfex.com** requires authentication within 1 minute of connecting.
* Attempting to re-authenticate an already authenticated connection yields `AlreadyAuthenticated`.

## Troubleshooting Checklist

1. Validate JSON (types/enums/required fields).
2. Respect rate limits (reduce burstiness; batch where possible).
3. Ensure your API key has the right permissions.
4. Log `incoming_message` from error frames to quickly reproduce issues.
5. For persistent `ServerError`, contact **[support@qfex.com](mailto:support@qfex.com)** with timestamp and offending `incoming_message`.
