Limits and errors
Every limit Relay enforces in one table, the three shapes an error takes, and the outcome of each call.
Limits#
| Limit | Value | Past the limit |
|---|---|---|
| Projects per account | 10 | A new project is refused. |
| Links per project | 10,000 | POST /v1/links answers LIMIT_REACHED. |
| Links created per IP address | 60 per minute | 429 RATE_LIMITED. |
| Link label | 64 characters | 400 VALIDATION_ERROR. |
| Calls per minute, per link | 60 by default, up to 6,000 in settings | Refused at once, never counted. |
initialize, tools/list and ping, per link | 300 per minute | 429 with Retry-After. |
| Requests with an unknown URL token, per IP address | 120 per minute | 429 with Retry-After. |
| MCP request body | 80 KiB | 413. |
| Calls in flight, per link | 8 | A tool error saying the app is busy. Refused at once, never counted. |
| Time to answer a call | 180 seconds | A tool error for the host and cancel for the app. Counted. |
Time to send hello | 10 seconds | Socket closed with 4400. |
Tools per hello | 64 | Socket closed with 4400. |
Manifest: app, instructions and tools as JSON | 64 KiB | Socket closed with 4400. |
| Tool name | 64 characters | Socket closed with 4400. |
| Tool description | 2,000 characters | Socket closed with 4400. |
| Instructions | 4,000 characters | Socket closed with 4400. |
ping frames and link.* commands, per link | 30 per minute | A rate_limited error frame; the frame is not executed and the socket stays open. |
| Invalid frames on one socket | 10 | Each gets an error frame; past the limit, the socket closes with 4400. |
| Call arguments as JSON | 64 KiB | -32602 before the app sees it, never counted. |
Result: content and structuredContent as JSON | 128 KiB | An invalid_result error frame for the app, and a tool error for the host. |
| Any frame | 256 KiB | Connection dropped with no close frame; the app sees 1006. Reconnect with backoff. |
| Silence on the socket | 90 seconds | Socket closed. Reconnect with backoff. |
| Call metadata kept | 30 days | Older calls leave the Calls tab. |
Three shapes of error#
Where an error shows up depends on who made the request.
Link creation: HTTP errors#
POST /v1/links answers errors with a status and an error object with a stable code. Every code is listed under Links.
⫻
401 Unauthorized
The app's socket: error frames and close codes#
A frame Relay cannot use gets an `error` frame. A problem with the link or the hello closes the socket with a code:
⫻
error
| Code | Meaning |
|---|---|
| 4400 | Invalid or late hello, an unsupported protocol, or more than 10 invalid frames on one socket. |
| 4401 | Unknown or revoked link token. Do not reconnect. |
| 4403 | Origin not allowed. Do not reconnect. |
| 4001 | A newer socket took over the link. |
| 4410 | The link was revoked. Do not reconnect. |
| 4423 | The project is suspended. Reconnect with backoff. |
| 4429 | Too many open sockets for the project. |
Reconnecting explains the backoff for the rest.
The host: tool errors and JSON-RPC errors#
When a call cannot be answered by the app, the host gets a tool result with isError: true that the model can repeat to the person. A call that is wrong in itself, such as an unknown tool or invalid arguments, gets JSON-RPC error -32602. Both are described under MCP endpoint.
⫻
200 OK
Call outcomes#
Each call in the dashboard's Calls tab carries one outcome. Only the counted outcomes use the allowance or credits.
| Outcome | Meaning | Counted |
|---|---|---|
| ok | The app answered with a result. | Yes |
| tool_error | The app answered with error. | Yes |
| timeout | No answer within 180 seconds. | Yes |
| cancelled | The host cancelled the call or went away after it reached the app. | Yes |
| disconnected | The app's socket closed during the call. | No, taken off the count |
| offline | The app was not connected. | No |
| paused | The link was paused. | No |
| busy | 8 calls were already in flight on the link. | No |
| rate_limited | Over the link's calls per minute. | No |
| invalid_input | The arguments did not match the tool's inputSchema. | No |
| unknown_tool | The tool is not in the app's list. | No |
| cap_reached | The call would have passed the project's spend cap. | No |
| quota_exceeded | The allowance was used up and the balance could not pay. | No |
- Raise a link's calls per minute in the project's settings when one person's use needs more.
- A run of
offlinecalls usually means the person closed the app, or a reconnect loop is failing; check the close codes your app receives.