Errors
HTTP, JSON-RPC, WebSocket and gRPC error codes, their causes and their fixes.
Errors come from two places. The constant-k gateway checks your key and your limits before a request reaches a node, and answers with an HTTP error status such as 401 or 403. The Solana node runs the method and answers with a JSON-RPC error object.
{
"jsonrpc": "2.0",
"error": { "code": -32602, "message": "Invalid param: WrongSize" },
"id": 1
}Some errors also carry a data field with details, such as simulation logs.
Gateway errors
The gateway answers with a JSON-RPC error body and "id": null, except the getTokenLargestAccounts concurrency 429, which echoes the request id. Placeholders in angle brackets are filled in with the method, the category or the limit that was hit.
| HTTP status | Code | Message | Cause and fix |
|---|---|---|---|
401 | -32600 | Missing API key | No key was sent. Add it to the URL or a header. See Authentication. |
403 | -32600 | Invalid API key | The key is wrong or was re-rolled. Copy it again from My Account. |
403 | -32600 | <method> requires higher tier | The method is not on your plan, for example getLatestBlockhash on Free. Upgrade, or call a method your plan includes. See Plans & Limits. |
403 | -32600 | <method> not supported | The method is not served on any plan: getLargestAccounts, sendBundle and a few methods Solana has removed. Retrying will not help. |
400 | -32600 | Batch requests are not supported | The body is a JSON array. Send one call per request. Even a one-element array is rejected. |
400 | -32600 | <method> requests a maximum of 100 items (<N> requested) | getMultipleAccounts and getSignatureStatuses take at most 100 items. Split the list. |
429 | -32005 | Global rate limit exceeded (<N>/sec, batch of <B>) | You went over your plan's total requests per second. |
429 | -32005 | Rate limit exceeded (<category>: <N>/sec) | You went over the cap for that group of methods, such as heavy block and history calls. |
429 | -32005 | Global <method> capacity reached (<N>/sec across all keys); retry shortly | That method is at capacity for every key, not only yours. Retry shortly. |
429 | -32005 | getProgramAccounts concurrency limit reached (per-key); retry shortly | Your API key already has as many getProgramAccounts and getProgramAccountsV2 calls in progress as its plan allows: 1 on Blade Runner, 3 on Nexus. Retry when one of them finishes. |
429 | -32005 | Node is unhealthy | Too many getTokenLargestAccounts requests were in progress at once, from your key or from all keys together, and this one could not start within 5 seconds. Retry after a short pause. |
503 | -32005 | getTokenLargestAccounts is temporarily unavailable; retry shortly | getTokenLargestAccounts is temporarily unavailable, or cannot be answered at the requested commitment at that moment. Retry shortly. |
503 | -32000 | a congestion message ending retry shortly | The region is shedding load. The response carries a Retry-After header. Wait that many seconds, then retry. |
A rate-limit 429 carries no wait time
Rate-limit responses (-32005) carry no Retry-After header and no X-RateLimit-* or RateLimit-* headers, so backoff is the client's own job. Budgets are counted in whole seconds and reset at the start of each new second, so a short wait is usually enough. See How rate limits work.
Long-term storage responses
On Blade Runner and Nexus tier plans, Cube Runner retrieves a block from long-term storage when no cache, ledger or other region holds it. A getBlock request returns the block when it is retrieved within 25 seconds. Otherwise the response is HTTP 429 with a Retry-After header and code -32019, and it echoes the request id.
| HTTP status | Code | Message | Cause and fix |
|---|---|---|---|
429 | -32019 | Block <slot> is being retrieved from long-term storage; retry in <N> seconds | The block is queued and stays queued. Wait Retry-After seconds, then send the same request again. See Queued requests. |
429 | -32019 | A message stating the reason | The block was not queued, was dropped from the queue, or was not returned by long-term storage. data.reason names the cause: queue_full, key_full, key_monthly, dropped or failed. See Responses that are not queued. |
Standard JSON-RPC errors
| Code | Message | What it means |
|---|---|---|
-32700 | Parse error | The body is not valid JSON. |
-32600 | Invalid request | The body is not a valid JSON-RPC 2.0 request. |
-32601 | Method not found | The method name is wrong or does not exist. Method names are case sensitive. |
-32602 | Invalid params | A parameter is missing or has the wrong type or format, for example a malformed address. |
-32603 | Internal error | The node failed while it handled the call. Retry with backoff. |
Solana node errors
These codes come from the node software, as do their messages.
| Code | Meaning | What to do |
|---|---|---|
-32001 | Block not available. For getBlock on Free and Operator tier plans, no cache, ledger or other region holds the block. For other methods, the block is older than the region's ledger. The message gives the first available block. | Ask for a slot at or above that block. On Blade Runner and Nexus tier plans, Cube Runner retrieves such a block from long-term storage instead. |
-32002 | Transaction simulation (preflight) failed. data holds the error and the logs. | Read data.logs, fix the transaction, then resend. See Transaction errors. |
-32003 | Transaction signature verification failed. | Sign after the message is final, with every required signer. |
-32004 | Block not available for this slot. | The block may not be ready yet. Retry shortly. |
-32005 | Node is unhealthy or behind. data may show how many slots behind. | Retry with backoff, or use another region. |
-32006 | Precompile verification failed (for example an Ed25519 or secp256k1 instruction). | Check the precompile instruction data. |
-32007 | The slot was skipped: no block was produced. | Normal on Solana. Move on to the next slot. |
-32008 | No snapshot is available. | Retry later. |
-32009 | The slot was skipped, or is missing in long-term storage. | Treat it as skipped. |
-32010 | The key is excluded from the account secondary index, so this method is unavailable for it. | Query this account another way, or contact support. |
-32011 | Transaction history is not available from this node. | Retry, or contact support if it persists. |
-32012 | Scan error, for example when an account scan returns too much data. | Add or tighten filters, and use dataSlice. |
-32013 | Transaction signature length mismatch. | The number of signatures does not match the message. Rebuild the transaction. |
-32014 | Block status is not available yet for this slot. | Retry shortly. |
-32015 | Unsupported transaction version. | Pass "maxSupportedTransactionVersion": 1. See Transaction version errors. |
-32016 | Minimum context slot not reached. The node has not yet seen the minContextSlot you asked for. | Retry shortly. |
-32017 | Epoch rewards are still being paid out. | Retry after the rewards period ends. |
-32018 | The slot is not an epoch boundary, so rewards cannot be found. | Check the epoch or slot you passed. |
-32019 | For getBlock, the block is queued for retrieval from long-term storage, or was not retrieved. See Long-term storage responses. | Wait Retry-After seconds when the response carries it, then send the same request again. Otherwise retry with backoff. |
-32020 | The transaction you filtered for was not found. | Check the signature. |
-32021 | No slot history. | Retry later. |
Transaction version errors
getBlock, getTransaction and blockSubscribe take a maxSupportedTransactionVersion parameter that says which formats your client can decode. Two errors come from getting it wrong.
| Code | Cause | Fix |
|---|---|---|
-32015 | The parameter is missing, or its value is below the version of a transaction in the response. The number in the message is the version the node found, not the value to send. | Send "maxSupportedTransactionVersion": 1. |
-32602 | The value is the string "1" instead of the integer 1. The node rejects the whole config object, so the message does not name the field. | Send the JSON number 1, with no quotes. |
The ceiling is raised after the client can decode the format, not before. Transaction v1 has the full error bodies, the client minimums and the migration steps.
Transaction errors
When preflight is on, a failed simulation returns -32002 and the send is rejected. The data object holds the transaction error and the program logs:
{
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "Transaction simulation failed: Error processing Instruction 2: custom program error: 0x1",
"data": {
"err": { "InstructionError": [2, { "Custom": 1 }] },
"logs": ["Program 11111111111111111111111111111111 invoke [1]", "…"]
}
},
"id": 1
}| Error | Cause |
|---|---|
BlockhashNotFound | The blockhash is too old or unknown to this node. A fresh one is needed. |
InsufficientFundsForFee | The fee payer cannot pay the fee. |
InstructionError with Custom | A program returned its own error code. The program's IDL or source defines it. |
AlreadyProcessed | This exact transaction already landed. A resend is redundant. |
With skipPreflight: true, the send succeeds even if the transaction would fail. getSignatureStatuses gives the result: a landed but failed transaction has a non-null err.
In @solana/kit, the error carries its own context:
import {
createSolanaRpc,
isSolanaError,
SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR,
} from "@solana/kit";
const rpc = createSolanaRpc(
`https://rpc-newyork.constant-k.com/?api-key=${process.env.CK_API_KEY}`,
);
// wireTx: a signed, base64-encoded transaction (see Sending Transactions)
try {
await rpc.sendTransaction(wireTx, { encoding: "base64" }).send();
} catch (e) {
if (isSolanaError(e, SOLANA_ERROR__JSON_RPC__SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE)) {
// Simulation failed: the logs usually say why
console.error(e.context.logs);
console.error(e.cause); // the transaction error
} else if (isSolanaError(e, SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR)) {
// Rejected by the gateway, for example 401 or 403
console.error("HTTP", e.context.statusCode);
} else {
throw e;
}
}WebSocket errors
A failed subscribe returns a JSON-RPC error frame with the codes above, for example -32602 for a bad address, and the connection stays open.
| Code | Cause | What to do |
|---|---|---|
Close 1006, right after the connection opens | The key is missing or invalid, or your plan does not include WebSockets. The handshake succeeds first, so there is no close frame. | Check the key and the plan. |
Close 1013 | You opened connections too fast, or you are at your plan's connection limit. | Read the close reason. Back off and reconnect. |
-32005 frame | You are over a rate limit or at the subscription cap. | Unsubscribe, or open another connection. |
-32602 frame on blockSubscribe | The options object is missing "maxSupportedTransactionVersion": 1, or sent it as a string. The check runs at subscribe time. | Send the integer 1. |
-32600 frame with "id": null, every few seconds | Your client is sending its own ping message. The stream is healthy. | Ignore it. See WebSockets. |
| Error frame for a non-subscription method | Only subscription methods and getHealth are allowed. | Send the call over HTTPS. |
gRPC errors
These apply to Kaldera, Turbo Kaldera and ShredPrism.
| Status | When | What to do |
|---|---|---|
UNAUTHENTICATED (16) | The x-token header is missing or wrong. The message is Missing or Invalid API Key. | Check the key and the header name. |
INVALID_ARGUMENT (3) | The subscribe request is malformed or breaks a filter limit. | Check the request against Filters and the plan limits. |
OUT_OF_RANGE (11) | In standard Yellowstone, fromSlot is older than the replay window. | Resubscribe with a newer slot, or without fromSlot. See Replay. |
UNAVAILABLE (14) | The connection dropped or the server is restarting. | Reconnect with exponential backoff. |
Some clients report a failed login differently:
@triton-one/yellowstone-grpc(TypeScript):failed to open subscribe stream.grpcurland other grpc-go tools:unexpected HTTP status code received from server: 204 (No Content).yellowstone-grpc-client(Rust):code: 'The request does not have valid authentication credentials', message: "Missing or Invalid API Key".
When to retry
| Retryable with exponential backoff | Not retryable until something changes |
|---|---|
Network errors, HTTP 429 and 5xx, -32603, -32005, -32004, -32014, -32016, -32019, gRPC UNAVAILABLE | 400, 401, 403, -32600, -32601, -32602, -32002, -32003, -32013, -32015, gRPC UNAUTHENTICATED and INVALID_ARGUMENT |
Backoff starts at about 250 ms, doubles each time, carries random jitter, and caps at a few seconds. A response with a Retry-After header sets the wait instead. WebSocket and gRPC streams use the same backoff, then resubscribe.