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

# Limits & Errors

> Rate limits, request size limits, and the error formats the Igris API returns.

## Rate limits

| Surface | Paths | Default | Keyed by | Override |
| - | - | - | - | - |
| Management API | everything except the gateways, `/health` and `/ready` | 100 requests per minute | Client IP | none |
| MCP gateway | `/v1/mcp/*` | 1000 requests per minute | API key | `IGRIS_GATEWAY_RATE_LIMIT` |
| LLM gateway | `/llm/*` | 1000 requests per minute | API key | `LLM_GATEWAY_GLOBAL_RATE_LIMIT_RPM` |

The two environment variables apply to self-hosted deployments. The management API limit uses the first address in `X-Forwarded-For`, falling back to `X-Real-IP`.

### 429 response

Each rate-limited response carries these headers:

```http theme={null}
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1760000000
```

`X-RateLimit-Reset` is a Unix timestamp in seconds. The body is:

```json theme={null}
{ "error": "Too many requests", "retryAfter": 42 }
```

This body is the same for the management API, the MCP gateway, and the LLM gateway's overall limiter. `retryAfter` is in seconds. The API does not send a `Retry-After` header. The SDK raises `IgrisRateLimitError` on 429.

### Policy rate limits

Rate limits in policy rules apply to LLM rules only. A request over a rule's window limit is denied with `429`:

```json theme={null}
{ "error": { "message": "Rate limit exceeded: ...", "type": "igris_error", "code": "429" } }
```

The call is logged as a policy denial. MCP tool rules can't carry a `limit`; the API rejects it with `422`. See [Policies](/govern/policies).

## Request size limits

| Surface | Limit |
| - | - |
| Management API (`/api/v1/*`) | 1 MB |
| MCP gateway (`/v1/mcp/*`) | 512 KB |
| LLM gateway (`/llm/*`) | 2 MB by default; `LLM_GATEWAY_BODY_LIMIT_BYTES` on self-hosted |

## Error format

Management API errors are JSON with an `error` field:

```json theme={null}
{ "error": "Member not found" }
```

Successful responses use `{ "data": ... }`, and lists add `meta` with `limit`, `offset`, and sometimes `total` and `nextCursor`.

### Status codes

| Status | Meaning |
| - | - |
| `400` | Bad request or failed validation (see below). |
| `401` | Missing or invalid credentials: `{ "error": "Unauthorized" }`. |
| `403` | Missing permission, plan limit, or you're no longer a member. |
| `404` | Resource not found. |
| `409` | Conflict, such as a concurrent change. |
| `422` | Validation failed on routes that return detailed errors, such as connections and policies. |
| `429` | Rate limited. |
| `500` | Server error. |

### Permission errors

A missing permission returns `403` and names the permission. See [Roles & permissions](/reference/permissions).

```json theme={null}
{ "error": "Forbidden", "required": "policies:write" }
```

### Validation errors

The status and shape depend on the route.

* **Default validation** (for example, `PATCH /api/v1/members/:userId/permissions`): `400` with the Zod result, `{ "success": false, "error": { ... } }`.
* **Connections and the policy create and stats routes**: `422` with `{ "success": false, "error": { "name": "ZodError", "message": "<JSON-encoded issues>" } }`.
* **Policy rule checks** (unsupported condition fields, `limit` on an MCP tool rule): `422` with a plain `{ "error": "message" }`.

### Plan errors

**REST routes** return a flat body with `403` when a feature isn't on your plan:

```json theme={null}
{
  "error": "plan_feature_unavailable",
  "feature": "archive-restore",
  "currentPlan": "starter",
  "upgradeTo": "growth"
}
```

Some older plan-gated routes return `{ "error": "Feature not available on your plan", "feature": "...", "currentPlan": "...", "requiredPlan": "..." }` instead.

**Auth endpoints** (inviting a member, changing a role) return `403` with a body that has only `message`, and `message` is a JSON string holding the same fields:

```json theme={null}
{
  "message": "{\"error\":\"plan_feature_unavailable\",\"feature\":\"rbac\",\"currentPlan\":\"growth\",\"upgradeTo\":\"scale\"}"
}
```

Parse `message` to read the fields. Inviting a member over the active-user cap returns `plan_limit_active_users` the same way, with `current`, `limit` and `upgradeTo`.

**Quota errors** on REST routes (policy count, rules per policy, retention) return `403`:

```json theme={null}
{
  "error": "Policy limit reached",
  "code": "PLAN_LIMIT_REACHED",
  "limit": "maxPolicies",
  "current": 2,
  "max": 2,
  "plan": "starter",
  "upgradeTo": "growth"
}
```

Starter allows 2 policies with 5 rules each by default.

## MCP gateway errors

The MCP gateway returns JSON-RPC 2.0 errors:

```json theme={null}
{ "jsonrpc": "2.0", "error": { "code": -32001, "message": "Policy denied: ..." }, "id": 1 }
```

| Code | HTTP | Meaning |
| - | - | - |
| `-32001` | 401 | Invalid API key. |
| `-32001` | 404 | Connection not found or not accessible. |
| `-32001` | 200 | Policy denied the tool call. The message starts with `Policy denied:`. |
| `-32000` | 200 | Agent session suspended by the kill switch, or upstream server error. |
| `-32000` | 400 | Stdio connections aren't supported, or upstream URL validation failed. |
| `-32000` | 500 | Internal error resolving the connection, or the HTTP connection has no upstream URL. |

The SDK raises `IgrisPolicyDeniedError` for policy denials.

## LLM gateway errors

Errors produced inside the LLM gateway, such as a policy rate limit, use an OpenAI-style body:

```json theme={null}
{ "error": { "message": "...", "type": "igris_error", "code": "429" } }
```

The gateway-level rate limit (`LLM_GATEWAY_GLOBAL_RATE_LIMIT_RPM`) is different. It runs before the gateway and returns the plain `{ "error": "Too many requests", "retryAfter": 42 }` body shown above.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.