# Gateway & streaming errors (/solana-rpc/errors)

> Corvus Solana JSON-RPC and gRPC error reference: exact codes, message prefixes, gRPC reasons, and which failures are retryable.



Match on three things together: the **transport status**, the **application code**, and the **stable message prefix**. Any gateway message can pick up a trailing support sentence (`Please contact support at discord.gg/corvus-labs`), and one of the codes deliberately covers more than one condition, so full-string equality won't hold.

## JSON-RPC and HTTP errors

What you get back is an ordinary JSON-RPC error object, usually with `id: null`:

```json
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "RPS limit exceeded"
  },
  "id": null
}
```

| HTTP |        JSON-RPC code | Message prefix                                        | Meaning                                         | Action                                      |
| ---: | -------------------: | ----------------------------------------------------- | ----------------------------------------------- | ------------------------------------------- |
|  400 |             `-32600` | `Invalid request body`                                | Malformed or unsupported request body           | Fix serialization; do not retry unchanged   |
|  401 |             `-32002` | `Unauthorized`                                        | Endpoint, IP, or token is not authenticated     | Fix access configuration                    |
|  403 | `-32003` or `-32004` | `You have no access to this service`                  | Resource does not include the requested service | Check plan/product access                   |
|  429 |             `-32001` | `RPS limit exceeded`                                  | Read allowance exhausted                        | Reduce concurrency and back off             |
|  429 |             `-32005` | `Transaction rate limit exceeded`                     | Transaction TPS exhausted                       | Pace sends and check signature before retry |
|  429 |             `-32005` | `Scan request timed out waiting for concurrency slot` | Too many concurrent scan-style requests         | Reduce scan concurrency; retry with jitter  |
|  502 |             `-32000` | `Unable to process Batch Request`                     | Batch could not be proxied                      | Split or retry idempotent batch items       |
|  503 |             `-32099` | `Server is not available at the moment`               | Transient service unavailability                | Retry idempotent work with bounded backoff  |

Two things in that table catch people out:

* `-32005` covers **two unrelated** conditions, and the prefix is the only thing telling a TPS rejection apart from a scan-concurrency one.
* `-32001` counts **weighted** units rather than requests. See [Solana RPC rate limits](/solana-rpc/limits).

`-32100` / `PPS limit requirement not met` isn't in the table because it never turns up in a JSON-RPC error body. It's a gRPC condition, covered under [PPS limit requirement not met](#pps-limit-requirement-not-met).

## gRPC errors

On the streaming side you get a standard gRPC status plus a machine-readable reason. Where there's a detail, it's JSON:

```json
{
  "reason": "stream_limit",
  "detail": "maximum concurrent streams exceeded"
}
```

`retry_after_ms` is optional and often missing. Stream, filter and subscription-rate rejections never carry it at all; the two-key `{reason, detail}` object above is all you get.

| gRPC status           | Code | Common reason                                                                       | Action                                                                                                                                                                       |
| --------------------- | ---: | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INVALID_ARGUMENT`    |    3 | `invalid_argument`, `malformed_subscribe`                                           | Fix the request; do not retry unchanged                                                                                                                                      |
| `PERMISSION_DENIED`   |    7 | `permission_denied`                                                                 | Verify product access                                                                                                                                                        |
| `RESOURCE_EXHAUSTED`  |    8 | `rate_limit`, `connection_limit`, `filters`, `subscribe_rate_limit`, `stream_limit` | Follow the detail, reduce load, then back off                                                                                                                                |
| `FAILED_PRECONDITION` |    9 | message beginning `from_slot replay`                                                | Replay unavailable on that connection; reconnect and retry, or re-subscribe without `from_slot`. See [replay on reconnect](/solana-rpc/grpc/usage#reconnect-after-a-failure) |
| `INTERNAL`            |   13 | `internal`                                                                          | Retry only under a bounded transient-error policy                                                                                                                            |
| `UNAVAILABLE`         |   14 | `service_unavailable`                                                               | Reconnect with backoff                                                                                                                                                       |
| `DATA_LOSS`           |   15 | message, e.g. `lagged`                                                              | The stream dropped data because the client fell behind; reconnect, optionally replaying with `from_slot`                                                                     |
| `UNAUTHENTICATED`     |   16 | `unauthenticated`                                                                   | Fix token or IP access before reconnecting                                                                                                                                   |

### Stream reset protection

If you open and cancel streams fast enough, HTTP/2 deals with you before gRPC gets a look in. The server sends a connection-level `GOAWAY` with error code `ENHANCE_YOUR_CALM` (11), and its debug payload is the bare string `RST_STREAM rate limit exceeded (potential rapid reset attack)`. There's no `grpc-status` and no JSON `reason`, and the whole connection goes rather than one stream. Match on the `GOAWAY`.

### PPS limit requirement not met

On streaming endpoints this one reaches you as a gRPC status rather than as a JSON-RPC error:

| Field                      | Value                                                                |
| -------------------------- | -------------------------------------------------------------------- |
| gRPC status                | `PERMISSION_DENIED` (7)                                              |
| Reason                     | `permission_denied`                                                  |
| Detail prefix              | `PPS limit requirement not met`                                      |
| Transport                  | aRPC streaming endpoint, `http://arpc.<region>.corvus-labs.io:20202` |
| After the rejection        | The server shuts the connection down                                 |
| Appears in a JSON-RPC body | Never                                                                |

The check has nothing to do with how much traffic you're actually sending. It runs once, when the connection is admitted: it adds the RPS and TPS on your plan together and looks for a total of 500. If you're short, raise RPS or TPS in the [dashboard](https://dashboard.corvus-labs.io). See [plan contents](/platform/pricing) and [aRPC limits](/arpc/limits#the-pps-check).

So `PERMISSION_DENIED` on a streaming endpoint means one of two things: the PPS condition above, or a resource that doesn't include the product you asked for. The detail prefix tells you which.

## Deciding whether to retry

| Class                            | Codes                                                                | Retry?                       | Rule                                                                                      |
| -------------------------------- | -------------------------------------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------- |
| Authentication or permission     | `-32002`, `-32003`, `-32004`, `UNAUTHENTICATED`, `PERMISSION_DENIED` | No                           | Change configuration first; a PPS close forces a new connection, but not an immediate one |
| Malformed request or filter      | `-32600`, `INVALID_ARGUMENT`                                         | No                           | Correct the payload first                                                                 |
| Rate or resource exhaustion      | `-32001`, `-32005`, `RESOURCE_EXHAUSTED`                             | Yes, after reducing pressure | Honor `retry_after_ms` when present; otherwise capped backoff with jitter                 |
| Service unavailable              | `-32099`, `-32000`, `UNAVAILABLE`, `INTERNAL`                        | Yes                          | Retry idempotent reads; reconnect streams and resend the full subscription set            |
| Ambiguous transaction submission | —                                                                    | Conditional                  | Query the signature, then resend the same signed bytes only while the blockhash is valid  |

[Reliability](/solana-rpc/reliability) goes further into per-code recovery, and [Troubleshooting](/solana-rpc/troubleshooting) starts from the symptom instead.
