# Errors & limits

> CardSdkError and every error code, what causes each and what to do, plus every limit of the card protocol in one table.

Source: https://docs.neurosquad.ai/en/card-sdk/api/errors

## `CardSdkError`

Every refusal — from the app, or from the SDK's own checks before a request leaves your card — is a
`CardSdkError`:

```ts
import { CardSdkError, isCardSdkError } from '@neurosquad/card-sdk'

async function saveReport(text: string): Promise<void> {
  try {
    await card.fs.writeText('reports/latest.md', text, { createDirs: true })
  } catch (error) {
    if (isCardSdkError(error, 'PERMISSION_DENIED')) {
      card.ui.toast(`This needs the ${error.permission} permission`)
    } else if (isCardSdkError(error, 'RATE_LIMITED')) {
      await new Promise((resolve) => setTimeout(resolve, error.retryAfterMs ?? 1000))
      return saveReport(text)
    } else if (error instanceof CardSdkError) {
      card.log.error(error.code, error.method, error.hostMessage, error.data)
    } else {
      throw error
    }
  }
}
```

| Property | What |
| --- | --- |
| `code` | One of the codes below. |
| `message` | `method: host message [CODE] — hint`, ready for the log. |
| `hostMessage` | The app's message alone (English, for developers — show your own text to users). |
| `method` | The method that failed, when it came from a call. |
| `data` | Details: schema issues, `{ permission }`, `{ retryAfterMs }`, `{ conflict }`… |
| `hint` | A one-line suggestion, for the codes that have one. |
| `permission` | For `PERMISSION_DENIED`: the missing permission. |
| `retryAfterMs` | For `RATE_LIMITED`: how long to wait. |
| `issues` | For `INVALID_PARAMS` / `BAD_REQUEST`: `{ path, keyword, message }[]` — where the params did not match. |

`isCardSdkError(error, code?)` is a type guard, optionally for one code.

## Error codes

| Code | Means | Usually |
| --- | --- | --- |
| `BAD_REQUEST` | The message was malformed or its params are not plain JSON. | A `Date`, `Map` or class instance in params — send plain data. |
| `INVALID_PARAMS` | Params do not match the method's schema. | See `error.issues` for the exact path. Also a port value that fails a schema, or a `{{secret:…}}` that is not set. |
| `METHOD_NOT_FOUND` | The app does not know this method. | An older app — check with `card.host.supports()`. |
| `PERMISSION_DENIED` | The package lacks the permission. | Declare it in the manifest, or ask with `card.permissions.request()` if optional. |
| `NOT_CONNECTED` | The target card or agent is not connected to yours by an arrow. | Ask the user to draw one. |
| `NOT_FOUND` | No such card, agent, key, file or port. | |
| `QUOTA_EXCEEDED` | A quota is used up: storage bytes or keys, spawned cards, file watchers, an agent's full prompt queue. | Delete old data, stop old watchers, wait. |
| `RATE_LIMITED` | Too many calls. `data.retryAfterMs` says how long to wait. | Batch, debounce, or back off. (Title, status, badge, overview and attention never reject for this — the SDK folds and retries them.) |
| `TOO_LARGE` | A message, value or response over its limit. | See [Limits](#limits). |
| `TIMEOUT` | No answer in time (a port request, a command, a fetch). | |
| `BUDGET_PAUSED` | The workspace is over its budget; programmatic prompts are refused. | The user raises the limit in the [Budget](https://docs.neurosquad.ai/en/cards/budget) card. |
| `BUSY` | The agent is mid-turn and you asked `whenBusy: 'fail'`. | |
| `HOST_NOT_ALLOWED` | `net.fetch` to a host outside the grant, or one that resolves to a blocked address. | Add the host to the `network` permission. |
| `NETWORK_ERROR` | DNS, connection, TLS or stream failure. | |
| `FS_DENIED` | A path outside the workspace folder, inside `.git/`, or through a link that leaves it. | Use a relative path. |
| `FS_ERROR` | Any other file problem; `data.conflict` when `ifMtimeMs` did not match. | |
| `NOT_VISIBLE` | Needs the card on screen: dialogs, links, focus, permission requests, prompts to an agent in dangerous mode. | Try again when `card.visibility === 'visible'`. |
| `USER_CANCELLED` | The user said no (a confirm, a prompt to a dangerous-mode agent), or a call was cancelled. | |
| `UNAVAILABLE` | The target is not running, the workspace is not open, the card is suspended, or the connection closed. | |
| `PROTOCOL_MISMATCH` | The card was built for a newer protocol than the app speaks. | The user updates NeuroSquad. |
| `NOT_READY` | A call was made before the handshake finished. | Wait for `connect()`. |
| `INTERNAL` | A bug in the app. | Report it with the card log. |

## Limits

All limits are in `card.limits` (the `LIMITS` constant of the SDK). The SDK checks the cheap ones
before sending; the app enforces all of them.

| Area | Limit |
| --- | --- |
| **Messages** | ≤ 1 MB each (`fs.write` and `net.fetch` requests, and `fs.read`, `fs.list`, `net.fetch` responses: ≤ 12 MB); ≤ 64 calls in flight (the SDK queues beyond that); 200 calls/s, bursts of 400; values ≤ 64 levels deep and ≤ 200 000 nodes |
| **Package** | archive ≤ 50 MB; unpacked ≤ 100 MB; ≤ 5 000 files; each ≤ 20 MB; paths ≤ 240 characters and 20 levels; manifest ≤ 256 KB; icon ≤ 128 KB |
| **Manifest** | ≤ 40 settings; ≤ 16 inputs and 16 outputs; ≤ 32 tools; ≤ 32 network hosts; schemas ≤ 500 nodes, 16 levels, `enum` ≤ 256 options and 16 KB, `const` ≤ 4 KB |
| **Storage** | key ≤ 256 characters; value ≤ 1 MB; ≤ 10 000 keys; 5 MB per card, 20 MB per package |
| **Network** | body ≤ 5 MB; response ≤ 10 MB; 6 at once; 120 a minute; ≤ 5 redirects; timeout 30 s (max 120 s), body included; ≤ 4 streams, each closed after 5 min of silence or 256 MB |
| **Files** | read and write ≤ 10 MB; list ≤ 5 000 entries; ≤ 20 watchers |
| **Agents** | prompt ≤ 20 000 characters, 6 a minute per card (method and port together), ≤ 10 queued per agent; command ≤ 20 000 characters; 30 commands a minute per card (`run` and the terminal port together, timeout ≤ 120 s), `write` 60 a minute; screen ≤ 500 lines; output delivered every 100 ms or 64 KB |
| **Tools** | result ≤ 1 MB; timeout 30 s (max 120 s); progress 4 a second |
| **Ports** | 20 messages/s per output; retained value ≤ 256 KB; request timeout 30 s (max 120 s) |
| **Card UI** | title ≤ 120 (30 a minute); status ≤ 80, status/badge/overview 120 a minute each; overview lines ≤ 160; toast ≤ 280 (one per 2 s); confirm text ≤ 1 000 (10 a minute); ≤ 12 menu items (30 changes a minute); `settings.set` 60 a minute; `tools.setEnabled` 30 a minute; `usage.summary` 6 a minute; attention every 10 s; focus every 5 s; resize every 500 ms; ≤ 4 spawned cards; clipboard once a second, ≤ 1 MB |
| **Log** | line ≤ 2 000 characters; 50 lines a second |
| **Frames** | ≤ 24 card pages running app-wide; ≤ 8 of them in the background; suspended after 60 s hidden, with 1 s to save; heartbeat every 5 s, "not responding" after 15 s; ready within 10 s of starting |
