> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.prividium.com/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

All RPC endpoints return errors as JSON-RPC 2.0 compliant responses with **HTTP 200** status. Error details are in the
response body.

## Error Codes

### Standard JSON-RPC 2.0

Defined in the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#error_object).

| Code       | Message          | Description                | When Used                                                                                                                                          |
| ---------- | ---------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| **-32600** | Invalid Request  | Malformed JSON-RPC request | Request doesn't match JSON-RPC format, or a batch exceeds the max size (20 requests per HTTP request by default)                                   |
| **-32601** | Method not found | Method does not exist      | Method is unknown to the proxy (typos, methods not supported by Prividium) or reported as `unimplemented` by the upstream sequencer.               |
| **-32602** | Invalid params   | Invalid method parameters  | Method called with wrong parameters, or `eth_sendRawTransaction` received an unsupported envelope (EIP-4844 blob or EIP-7702 set-code transaction) |
| **-32603** | Internal error   | Unhandled server error     | Unexpected error in request processing                                                                                                             |

### Custom Prividium Codes

These are implementation-specific codes for authentication/authorization, not part of JSON-RPC 2.0 spec.

| Code       | Message      | Description            | HTTP Equivalent | When Used                                |
| ---------- | ------------ | ---------------------- | --------------- | ---------------------------------------- |
| **-32090** | Unauthorized | User not authenticated | 401             | Missing or invalid auth token or API key |
| **-32001** | Forbidden    | User not authorized    | 403             | Valid auth but insufficient permissions  |

> **Note:** Custom codes use the implementation-defined range.

A `-32090` caused by the `X-API-Key` header carries a specific message: `Malformed API key: …` for a value that is not a
well-formed key, `Multiple X-API-Key headers` when the header is repeated, or `API key rejected` for a key Prividium
cannot use. Its `data` holds `keyPrefix`, the first 12
characters of the key sent, and `clientIp`, the source IP Prividium saw when that address is public. See
[Developer troubleshooting](/developers/applications/developer-troubleshooting#api-key-errors).

A gated method called with neither an API key nor a session token returns `-32090` with the message
`Authentication required: send an X-API-Key header or a session token`. `eth_chainId` and other public methods succeed without authentication. To check credentials,
call a gated method such as `eth_getBalance`.

**Examples:**

Calling an unknown / unregistered method:

```
--> {"jsonrpc": "2.0", "method": "eth_doesNotExist", "params": [], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method eth_doesNotExist not found"}, "id": 1}
```

RPC call without authentication:

```
--> {"jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "latest"], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32090, "message": "Authentication required: send an X-API-Key header or a session token"}, "id": 1}
```

RPC call with a malformed API key (73 characters instead of 72):

```
--> {"jsonrpc": "2.0", "method": "eth_getBalance", "params": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "latest"], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32090, "message": "Malformed API key: expected priv_sk_ followed by 64 letters or digits (72 characters), got 73", "data": {"clientIp": "203.0.113.7", "keyPrefix": "priv_sk_Ab12"}}, "id": 1}
```

RPC call with valid auth but insufficient permissions:

```
--> {"jsonrpc": "2.0", "method": "eth_sendTransaction", "params": [{"from": "0x...", "to": "0x..."}], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32001, "message": "Forbidden"}, "id": 1}
```

Raw transaction with an unsupported envelope (here an EIP-7702 set-code transaction):

```
--> {"jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0x04f8..."], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32602, "message": "Unsupported eip7702 transaction"}, "id": 1}
```

For debugging -32090 and -32001 and related authentication issues, see
[Developer troubleshooting](/developers/applications/developer-troubleshooting).

## Chain errors on reads

When the chain rejects a read that Prividium filters for the caller (`eth_getLogs`, block reads and similar), Prividium
returns the chain's error only when it carries no chain data. These errors keep code **-32602** and the chain's message,
without `data`. Any other chain error returns **-32603** `Internal error`. `eth_getTransactionReceipt`,
`eth_getTransactionByHash` and `eth_getBlockReceipts` return the chain's error unchanged.

| Message                                                              | When Used                                                      |
| -------------------------------------------------------------------- | -------------------------------------------------------------- |
| `query exceeds max block range N`                                    | The `eth_getLogs` block range is wider than the chain allows   |
| `query returned too many results, narrow the block range`            | `eth_getLogs` matched more logs than the chain returns at once |
| `log filter exceeds the limit of N addresses or topics per position` | The `eth_getLogs` filter lists too many addresses or topics    |
| `block not found` or ``block `<block>` not found``                   | The requested block does not exist yet                         |
| `Invalid params`                                                     | The chain could not decode the parameters                      |

These are client errors: retrying the same request returns the same error. Narrow the block range or fix the parameters
instead.

```
--> {"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"fromBlock": "0x1", "toBlock": "0x100000"}], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32602, "message": "query exceeds max block range 100000"}, "id": 1}
```

## Chain-side errors on `eth_sendRawTransaction`

On zksync-os chains, `eth_sendRawTransaction` is forwarded through the chain's `eth_sendRawTransactionSync` so
authoritative block-build rejections surface as JSON-RPC errors instead of stuck-in-mempool txs. Callers should handle
two extra codes on this method in addition to the standard and Prividium auth codes above.

| Code       | Message                                    | When Used                                                                                                                                                  |
| ---------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **-32003** | `Transaction rejected during execution: …` | The VM rejected the tx at block-build. Includes policy denials (`FilteredByValidator`) and other `InvalidTransaction` variants. The tx will not be mined.  |
| **-32005** | Sequencer rate limit                       | The chain is rate limiting and did not admit the tx. It was not rejected. Retry it. `error.data.retryAfterMs` carries the suggested delay in milliseconds. |

**Examples:**

Tx rejected at block-build (e.g. policy denial or insufficient balance after intervening txs):

```
--> {"jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0x02f8..."], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32003, "message": "transaction rejected during execution: FilteredByValidator"}, "id": 1}
```

Tx admitted but not mined within the chain's sync timeout. The proxy returns the tx hash, as a plain
`eth_sendRawTransaction` does; the tx may still mine, so poll `eth_getTransactionReceipt` and do not resubmit it:

```
--> {"jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0x02f8..."], "id": 1}
<-- {"jsonrpc": "2.0", "result": "0x5c50...", "id": 1}
```

A caller with full sequencer access that calls `eth_sendRawTransactionSync` directly still gets the EIP-7966 timeout
error, code `4`.

Tx not admitted because the chain is rate limiting (caller waits `retryAfterMs`, then resubmits the same raw tx):

```
--> {"jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params": ["0x02f8..."], "id": 1}
<-- {"jsonrpc": "2.0", "error": {"code": -32005, "message": "executed-gas rate limiter: bank exhausted", "data": {"retryAfterMs": 1200}}, "id": 1}
```

The message varies with which of the chain's limiters tripped — the request-rate limiter or the executed-gas limiter.
Branch on the code and `retryAfterMs`, never on the message. A transaction refused this way never reached the mempool,
so it is not the same outcome as a transaction the sequencer rejected.

## HTTP 429 Rate Limit Error

Prividium's own request rate limit is separate from the chain's `-32005` above, and is returned as an HTTP response
with status code 429 rather than a JSON-RPC error.

| HTTP Status | Error Code         | Description                                      |
| ----------- | ------------------ | ------------------------------------------------ |
| **429**     | `RATE_LIMIT_ERROR` | Per-window quota or RPC concurrency cap exceeded |

**Response format:**

```json
{
  "error": {
    "code": "RATE_LIMIT_ERROR",
    "message": "Rate limit exceeded. Try again in 45 seconds."
  }
}
```

**Response headers:**

| Header                  | Description                                       |
| ----------------------- | ------------------------------------------------- |
| `x-ratelimit-limit`     | Maximum quota units allowed in the current window |
| `x-ratelimit-remaining` | Quota units remaining in the current window       |
| `x-ratelimit-reset`     | Seconds until the current window resets           |
| `retry-after`           | Seconds to wait before retrying                   |

For RPC quotas, each batch item consumes one unit. A concurrency rejection returns `retry-after: 1`; quota headers appear only if a quota check applied. No operations in the rejected request execute. See [RPC Batch and Concurrency Caps](/configuring/applications/rate-limiting#rpc-batch-and-concurrency-caps).
