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

# Errors and limits

> JSON-RPC error codes, HTTP statuses, per-plan rate limits, and how plan-gated tools fail on Free.

The MCP route follows the JSON-RPC spec for errors and maps a few categories onto HTTP statuses. This page is the reference for what each error means and what your agent (or client) should do about it.

## Error codes

| Code     | Meaning        | When                                                                                                        |
| -------- | -------------- | ----------------------------------------------------------------------------------------------------------- |
| `-32001` | Auth           | Missing/invalid `Authorization` header or the `cb_live_*` key was revoked.                                  |
| `-32002` | Rate-limit     | Per-key sliding-window cap exceeded.                                                                        |
| `-32602` | Invalid params | Tool arg shape didn't match the JSON Schema.                                                                |
| `-32004` | Not found      | Tool name unknown, or the requested resource doesn't exist in this workspace.                               |
| `-32603` | Internal       | Catch-all server error. **Also used for plan-gated tool calls** — the message body contains `PRO_REQUIRED`. |

## HTTP status mapping

| Situation            | HTTP status | Body shape                                              |
| -------------------- | ----------- | ------------------------------------------------------- |
| Auth failure         | `401`       | JSON-RPC error envelope                                 |
| Rate-limit           | `429`       | JSON-RPC error envelope, includes a friendly retry hint |
| Any tool-level error | `200`       | JSON-RPC error in the body (per MCP spec)               |
| Healthy response     | `200`       | JSON-RPC `result`                                       |

The spec calls for tool errors to come back as `200` with the error inside the JSON-RPC envelope — clients that conflate HTTP status with "did the call succeed?" will misread these. The MCP SDKs all do the right thing.

## Plan-gated tools

Some tools are Builder-only. On a Free workspace they return `-32603` with a `PRO_REQUIRED` message that names the gated feature. The full list lives in the [tool reference](/mcp/tool-reference) — look for the **Builder plan required** annotation in each tool's description.

Launch beta highlights:

* `blocks.create` — AI block generator. Free path: hand-author in the in-app editor.
* `schedule.set`, `schedule.pause`, `schedule.resume`, `schedule.delete`, `schedule.preview`, `schedule.runNow` — entire scheduled-run surface is Builder-only.
* `dataSources.add`, `dataSources.editRefreshPolicy`, `dataSources.introspect`, `dataSources.testConnection`, `dataSources.triggerRefresh`, `dataSources.listMcpTools` — data connections are Builder beta. Free workspaces can still list, read, and delete existing data sources.
* `byokKeys.add` — BYOK is Builder beta. Free workspaces can still list or delete existing BYOK key metadata.
* `stores.connect`, `stores.disconnect`, `stores.refreshProducts` — stores are Builder beta.

<Tip>
  Coding agents: tool descriptions ending in `(Builder plan required.)` are gated server-side. Skip these on Free workspaces — call `workspace.get` first to read `plan` and `limits`.
</Tip>

## Rate limits

Per-key sliding-window via `@convex-dev/rate-limiter`. The cap is resolved from `workspace.plan` at request time:

| Plan       | Requests per minute (per key) |
| ---------- | ----------------------------- |
| Free       | 60                            |
| Builder    | 600                           |
| Enterprise | 6000                          |

Different keys for the same workspace get **independent budgets** — minting a second key buys you another 60/600/6000 RPM. The platform doesn't penalize per-workspace, just per-key.

When you hit the cap, the next call returns `-32002` with HTTP `429` and a hint that includes the resume-time. Back off by \~`60 / RPM` seconds and retry.

## Periodic limits

Two limits are time-windowed rather than rate-limited:

| Limit                         | Free | Builder | Enterprise | Reset                   |
| ----------------------------- | ---- | ------- | ---------- | ----------------------- |
| Builds per month              | 100  | 1,000   | 100,000    | First of next month UTC |
| AI-generated blocks per month | 0    | 30      | 10,000     | First of next month UTC |

Builds count every call to `blocks.build` (success or failure). AI-generated blocks count every `blocks.create` invocation.

When you hit either, the next call returns `-32603` with a `LIMIT_REACHED` message. Both limits expose `resetsAt` on the `workspace.get` `limits` payload, so agents can preflight.

## Per-resource count caps

Create-time caps for workspace-owned resources. Hitting any of these returns `LIMIT_REACHED` from the relevant tool:

| Resource                | Free | Builder   | Enterprise |
| ----------------------- | ---- | --------- | ---------- |
| Blocks                  | 10   | 100       | 10,000     |
| Canvases                | 2    | 20        | 1,000      |
| Scheduled runs          | 0    | 5         | 500        |
| Data sources (per type) | 0    | 10        | 100        |
| BYOK keys               | 0    | 3         | 50         |
| Store blocks            | 0    | 3         | unlimited  |
| Workspace members       | 1    | unlimited | unlimited  |

See [Plans and limits](/concepts/plans-and-limits) for the full table.

## Preflight via `workspace.get`

The cheapest way to avoid a 4xx is to read `limits` first. `workspace.get` returns:

```json theme={null}
{
  "id": "...",
  "name": "...",
  "plan": "free",
  "limits": {
    "blocks": { "used": 7, "max": 10, "resetsAt": null },
    "buildsPerMonth": { "used": null, "max": 100, "resetsAt": "2026-06-01T00:00:00Z" },
    "members": { "used": 1, "max": null, "resetsAt": null }
  }
}
```

* `max: null` means `Infinity` (members on Builder/Enterprise).
* `used: null` on periodic limits means the rate-limiter doesn't expose current consumption — only the cap.
* `resetsAt: null` on count-based limits means there's no time-based reset.

## What's next

<CardGroup cols={2}>
  <Card title="Setup" icon="terminal" href="/mcp/setup">
    Per-client configs for connecting Claude / Codex / Cursor / Windsurf.
  </Card>

  <Card title="Tool reference" icon="list" href="/mcp/tool-reference">
    Auto-generated list of every tool, with arg shapes and plan gates.
  </Card>
</CardGroup>
