Skip to Content
Card SDKAPI referenceErrors & limits

Errors & limits

CardSdkError

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

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 } } }
PropertyWhat
codeOne of the codes below.
messagemethod: host message [CODE] — hint, ready for the log.
hostMessageThe app’s message alone (English, for developers — show your own text to users).
methodThe method that failed, when it came from a call.
dataDetails: schema issues, { permission }, { retryAfterMs }, { conflict }…
hintA one-line suggestion, for the codes that have one.
permissionFor PERMISSION_DENIED: the missing permission.
retryAfterMsFor RATE_LIMITED: how long to wait.
issuesFor 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

CodeMeansUsually
BAD_REQUESTThe message was malformed or its params are not plain JSON.A Date, Map or class instance in params — send plain data.
INVALID_PARAMSParams 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_FOUNDThe app does not know this method.An older app — check with card.host.supports().
PERMISSION_DENIEDThe package lacks the permission.Declare it in the manifest, or ask with card.permissions.request() if optional.
NOT_CONNECTEDThe target card or agent is not connected to yours by an arrow.Ask the user to draw one.
NOT_FOUNDNo such card, agent, key, file or port.
QUOTA_EXCEEDEDA 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_LIMITEDToo 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_LARGEA message, value or response over its limit.See Limits.
TIMEOUTNo answer in time (a port request, a command, a fetch).
BUDGET_PAUSEDThe workspace is over its budget; programmatic prompts are refused.The user raises the limit in the Budget card.
BUSYThe agent is mid-turn and you asked whenBusy: 'fail'.
HOST_NOT_ALLOWEDnet.fetch to a host outside the grant, or one that resolves to a blocked address.Add the host to the network permission.
NETWORK_ERRORDNS, connection, TLS or stream failure.
FS_DENIEDA path outside the workspace folder, inside .git/, or through a link that leaves it.Use a relative path.
FS_ERRORAny other file problem; data.conflict when ifMtimeMs did not match.
NOT_VISIBLENeeds the card on screen: dialogs, links, focus, permission requests, prompts to an agent in dangerous mode.Try again when card.visibility === 'visible'.
USER_CANCELLEDThe user said no (a confirm, a prompt to a dangerous-mode agent), or a call was cancelled.
UNAVAILABLEThe target is not running, the workspace is not open, the card is suspended, or the connection closed.
PROTOCOL_MISMATCHThe card was built for a newer protocol than the app speaks.The user updates NeuroSquad.
NOT_READYA call was made before the handshake finished.Wait for connect().
INTERNALA 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.

AreaLimit
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
Packagearchive ≤ 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
Storagekey ≤ 256 characters; value ≤ 1 MB; ≤ 10 000 keys; 5 MB per card, 20 MB per package
Networkbody ≤ 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
Filesread and write ≤ 10 MB; list ≤ 5 000 entries; ≤ 20 watchers
Agentsprompt ≤ 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
Toolsresult ≤ 1 MB; timeout 30 s (max 120 s); progress 4 a second
Ports20 messages/s per output; retained value ≤ 256 KB; request timeout 30 s (max 120 s)
Card UItitle ≤ 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
Logline ≤ 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