Error Reference
Nodius returns errors via HTTP status codes and JSON-RPC error objects. This guide covers all error conditions, their meanings, and how to handle them.
Important โ numeric code collisions: Nodius proxy error codes and upstream Solana node error codes share the same numeric range (
-32001through-32015). This is an inherent consequence of JSON-RPC 2.0's server error code range and cannot be avoided without breaking compatibility with Solana RPC clients. A numeric code alone is not sufficient to determine the error source. SDKs and automated systems must use the combination of HTTP status code + numeric code + error message to disambiguate. The Proxy Error Codes table below separates proxy-generated codes from upstream Solana codes to make this explicit.
Proxy Error Codes (JSON-RPC)
Proxy error constants are stable and will not change without versioned deprecation. These are returned in the JSON-RPC error object or as HTTP error responses.
| Code | HTTP | Constant | Credits consumed? | Retry? | Retry delay | SDK error class |
|---|---|---|---|---|---|---|
| -32010 | 402 | INSUFFICIENT_CREDITS |
No | After deposit | โ | InsufficientCreditsError |
| -32002 | 402 | PAYMENT_REQUIRED |
No | After deposit | โ | InsufficientCreditsError |
| -32005 | 429 | RATE_LIMITED |
No | Yes | Retry-After header (โฅ1s) |
RateLimitError |
| -32005 | 429 | CONCURRENCY_LIMITED |
No | Yes | 1s + jitter | RateLimitError |
| -32003 | 502 | BACKEND_UNAVAILABLE |
No | Yes | 2s + exp. backoff | BackendUnavailableError |
| -32003 | 503 | SERVICE_TEMPORARILY_UNAVAILABLE |
No | Yes | 5s + exp. backoff | BackendUnavailableError |
| -32001 | 403 | METHOD_NOT_ALLOWED |
No | No | โ | MethodNotAllowedError |
| -32004 | 200 | SERVICE_PROFILE_DISABLED |
No | No | โ | ServiceProfileDisabledError |
| โ | 403 | NONCE_REUSED |
No | No (regenerate nonce) | โ | AuthenticationError |
| โ | 401 | AUTH_FAILED |
No | No (fix credentials) | โ | AuthenticationError / ExpiredSessionError |
| โ | 400 | INVALID_REQUEST |
No | No (fix request) | โ | NodiusError |
| -32601 | 200 | METHOD_NOT_FOUND |
No | No | โ | MethodNotAllowedError / RpcError |
| -32602 | 200 | INVALID_PARAMS |
No | No (fix params) | โ | RpcError |
| -32603 | 200 | INTERNAL_ERROR |
No | Yes | 1s + backoff | RpcError |
Note: -32005 is used for both rate limiting and concurrency limiting. Distinguish by the error message text: "rate limit exceeded" vs "concurrency limit exceeded". -32003 is used for both 502 (backend unreachable) and 503 (transient unavailability); distinguish by HTTP status code.
Why some JSON-RPC failures return HTTP 200
JSON-RPC 2.0 is a transport-agnostic protocol that communicates success and failure inside the response body (result vs error), not via HTTP status. When the proxy successfully processes a request and the upstream Solana node returns a JSON-RPC error (e.g., "method not found", "block not available"), the proxy delivers that error inside a 200 OK response โ the HTTP layer succeeded, even though the RPC call failed. Proxy-level errors that prevent the request from reaching the node (auth failure, no credits, rate limit) use appropriate HTTP status codes (401, 402, 429). The one exception is SERVICE_PROFILE_DISABLED: it returns HTTP 200 because the proxy deliberately rejected the call before billing, and the response is a valid JSON-RPC error object.
SDK recommendation: Both SDKs expose typed error classes that encapsulate the disambiguation logic. SDK consumers should catch typed exceptions rather than parsing numeric codes or message text directly.
HTTP Status Codes
200 OK
Request succeeded. The response body contains the JSON-RPC result. Note that a 200 status can still contain a JSON-RPC error (e.g., method not found) โ always check the error field in the response.
400 Bad Request
The request body is malformed or missing required fields.
{
"error": {
"code": "INVALID_REQUEST",
"message": "Request body must be valid JSON"
}
}
Common causes:
- Invalid JSON syntax
- Missing jsonrpc, method, or id fields
- Invalid method parameters
- Batch size exceeds maximum (20)
401 Unauthorized
Authentication failed.
{
"error": {
"code": "AUTH_FAILED",
"message": "Invalid signature"
}
}
Common causes: - Missing authentication headers - Invalid Ed25519 signature - Timestamp outside +-60 second window - Expired session token - Malformed public key
Troubleshooting:
- Verify your system clock is synchronized (use NTP)
- Ensure you're signing the correct payload format: solana-nodius:v2:{method}:{path}:{timestamp}:{nonce}:{body_hash}.
- Check that the body hash matches the actual request body
- Verify the signed path is the URL path only, without query string.
402 Payment Required
No credits available โ either your pubkey has no account record yet, or your balance is zero.
When no account record exists (no deposit has landed and POST /account/api-key was never called), the response includes deposit instructions:
{
"jsonrpc": "2.0",
"id": null,
"error": {
"code": -32002,
"message": "payment required: deposit USDC to activate your account",
"data": {
"deposit_address": "CQKviPv5QupKhE6MuhSuqP4uh1JfrL93nxtcx23tLMRk",
"usdc_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"minimum_deposit_usdc": "0.01",
"network": "solana-mainnet",
"instructions": "Send USDC (minimum $0.01) to the deposit address from your wallet. Your account is created automatically when the deposit confirms."
}
}
}
| Field | Value |
|---|---|
| Token | USDC (SPL) |
| Chain | Solana mainnet |
| USDC mint | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| Deposit address | CQKviPv5QupKhE6MuhSuqP4uh1JfrL93nxtcx23tLMRk |
| Minimum deposit | $0.01 USDC |
โ ๏ธ Do not send SOL or any token other than USDC. Only SPL USDC transfers are credited. Other tokens sent to the deposit address are permanently lost.
When the account record exists but the balance is zero or depleted:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32010,
"message": "insufficient credits"
}
}
Resolution: Send USDC (SPL, minimum $0.01) on Solana mainnet to the deposit address from your wallet. Credits appear within ~30 seconds of on-chain confirmation. Check your balance via GET /account/info. With x402 auto-pay enabled in the SDK, the 402 triggers an automatic micro-deposit and the call is retried.
403 Forbidden
The request is authenticated but not authorized.
{
"error": {
"code": "METHOD_NOT_ALLOWED",
"message": "method not allowed: requestAirdrop"
}
}
Common causes:
- Replayed nonce (same nonce used within 60-second window)
- Calling a denied method (requestAirdrop)
- Account suspended
{
"error": {
"code": "NONCE_REUSED",
"message": "Nonce has already been used"
}
}
404 Not Found
Account or resource not found.
{
"error": {
"code": "ACCOUNT_NOT_FOUND",
"message": "No account found for pubkey. Deposit USDC or call POST /account/api-key"
}
}
429 Too Many Requests
Rate limit exceeded.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 1 second."
}
}
Headers:
Retry-After: 1
X-Ratelimit-Limit: 50
X-Ratelimit-Remaining: 0
X-Ratelimit-Reset: 1712000001
Resolution: Wait for the duration specified in Retry-After before retrying. Implement exponential backoff for repeated 429s.
500 Internal Server Error
Something went wrong on the server.
{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"requestId": "req_abc123"
}
}
Resolution: Retry with backoff. If the issue persists, contact support with the requestId.
502 Bad Gateway
The upstream Solana node is unreachable.
{
"error": {
"code": "UPSTREAM_ERROR",
"message": "Solana node unavailable"
}
}
Resolution: Retry after a brief delay. Check /health for node status.
503 Service Unavailable
The service is temporarily unavailable. This can happen during startup, upstream failure, overload, accounting dependency failure, or critical disk pressure. Transient service-gate denials happen before billing and include a retry hint when available.
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "hot_rpc service temporarily unavailable due to critical disk pressure"
}
}
Resolution: Retry with exponential backoff. Check nodius.xyz/status.
504 Gateway Timeout
The request exceeded its method-dependent timeout. See Request Timeouts for details.
Response Headers
All authenticated responses (including errors) include:
| Header | Description |
|---|---|
| X-Credits-Remaining | Current credit balance after this request |
| X-Credits-Low | Present and set to "true" when balance < 1000 |
| X-Request-Id | Unique identifier for debugging |
Rate limit responses (429) include:
| Header | Description |
|---|---|
| Retry-After | Seconds to wait before retrying |
| X-Ratelimit-Limit | Maximum requests per second for this account |
| X-Ratelimit-Remaining | Remaining requests in current window |
| X-Ratelimit-Reset | Unix timestamp when the rate limit resets |
Rate-limit headers (X-Ratelimit-*) are also present on non-429 authenticated responses. Public endpoints (/health, /pricing, /capabilities) do not include any of these headers.
JSON-RPC Error Codes
These errors are returned inside a 200 OK response, in the JSON-RPC error field. They originate from the Solana node or the proxy's JSON-RPC handling.
Standard JSON-RPC Error Codes
| Code | Description |
|---|---|
| -32700 | Parse error: invalid JSON |
| -32600 | Invalid request: missing required fields |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
-32700: Parse Error
{
"jsonrpc": "2.0",
"id": null,
"error": {
"code": -32700,
"message": "Parse error: invalid JSON"
}
}
The request body is not valid JSON.
-32600: Invalid Request
{
"jsonrpc": "2.0",
"id": null,
"error": {
"code": -32600,
"message": "Invalid request: missing 'method' field"
}
}
The JSON is valid but doesn't conform to the JSON-RPC 2.0 specification.
-32601: Method Not Found
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found: getInvalidMethod"
}
}
The requested method is not supported. Check the API Reference for supported methods.
-32602: Invalid Params
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params: expected base58 pubkey"
}
}
Method parameters are invalid. Check the Solana JSON-RPC documentation for the correct parameter format.
-32603: Internal Error
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32603,
"message": "Internal error"
}
}
An unexpected error occurred in the RPC handler.
-32000 to -32099: Server Errors
Solana-specific errors that pass through from the upstream Agave node:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32002,
"message": "Transaction simulation failed: Blockhash not found"
}
}
Upstream Solana node error codes:
| Code | Typical Message | Description |
|---|---|---|
| -32001 | Node is behind by X slots |
Node is not caught up |
| -32002 | Transaction simulation failed |
Transaction would fail |
| -32003 | Transaction signature verification failure |
Bad transaction signature |
| -32004 | Block not available |
Requested block has been purged from the node's ledger |
| -32005 | Node is unhealthy |
Node health check failed |
| -32006 | No snapshot available |
Snapshot not available |
| -32007 | Long-running operation |
Request timed out on the node |
| -32009 | Slot was skipped |
Requested slot has no block |
| -32014 | Minimum slot not reached |
Slot not yet reached |
| -32015 | Unsupported transaction version |
Version incompatibility |
Distinguishing proxy errors from Solana errors: The same numeric code can come from either source. Use the HTTP status code and error constant to disambiguate:
- HTTP 200 with
-32004and message containing "service" or "profile" โ proxySERVICE_PROFILE_DISABLED(the method requires an endpoint profile that is not enabled; no credits consumed) - HTTP 200 with
-32004and message containing "Block" or "not available" or "cleaned up" โ upstream Solana (the requested slot was purged from the node's ledger) - HTTP 502 with
-32003โ proxyBACKEND_UNAVAILABLE(upstream node unreachable) - HTTP 200 with
-32003and message containing "signature verification" โ upstream Solana (bad transaction signature) - HTTP 429 with
-32005โ proxyRATE_LIMITEDorCONCURRENCY_LIMITED - HTTP 200 with
-32005and message containing "unhealthy" โ upstream Solana
Request Timeouts
Requests have method-dependent timeouts:
| Category | Timeout | Examples |
|---|---|---|
| READ_CHEAP | 5 seconds | getSlot, getBalance, getAccountInfo |
| READ_MEDIUM | 15 seconds | getTransaction, simulateTransaction |
| READ_HEAVY / ARCHIVE | 30 seconds | Heavy indexed/archive endpoints when enabled |
| SEND | 10 seconds | sendTransaction |
| SEND_BUNDLE | 10 seconds | sendBundle |
If a request exceeds its timeout, the proxy returns a 504 Gateway Timeout or a JSON-RPC error with code -32007.
WebSocket Error Codes
Close Codes
| Code | Reason | Description | Action |
|---|---|---|---|
| 1000 | normal |
Clean close by client | No action |
| 1008 | policy_violation |
Invalid or expired token | Re-authenticate |
| 4001 | auth_failed |
WebSocket authentication failed | Check credentials |
| 4002 | insufficient_credits |
Credit balance below minimum (10) | Deposit more USDC |
| 4003 | rate_limited |
Too many messages per second | Reduce message rate |
| 1006 | abnormal |
Network interruption | Reconnect with backoff |
Subscription Errors
Returned as JSON-RPC error messages on the WebSocket:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "Subscription limit reached (100/100)"
}
}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "method not allowed: requestAirdrop"
}
}
Common Troubleshooting
"Invalid signature" on every request
- Check your clock: Your system time must be within ยฑ60 seconds of UTC. Run
ntpdateor check your NTP configuration. - Verify the signing payload: Use
solana-nodius:v2:{method}:{path}:{timestamp}:{nonce}:{body_hash}. The body hash is SHA-256 of the raw request body string. - Ensure you're signing with the correct key: The public key in
X-Pubkeymust correspond to the private key used for signing. - Check encoding: The signature should be base58-encoded. The public key should be base58-encoded.
- Check the path: Sign the URL path only, without query string.
"Account not found" after creating keypair
Your pubkey has no account record yet. Either:
1. Send USDC (SPL) on Solana mainnet to the deposit address (CQKviPv5QupKhE6MuhSuqP4uh1JfrL93nxtcx23tLMRk, min $0.01) โ credits appear within seconds and the record is created automatically.
2. Or call POST /account/api-key with wallet sig to create the record (zero balance) and get an API key.
await rpc.generateApiKey();
"Insufficient credits" but I sent a deposit
- Deposits require finalized confirmation on Solana (~30 seconds)
- Check that you sent USDC from your registered wallet (the same pubkey you use for wallet-signature auth)
- Verify the deposit sent enough USDC to produce credits; see GET /pricing for the current rate
- Try confirming the deposit manually via
POST /account/confirm-deposit - Check your balance via
GET /account/info
Rate limited despite low request volume
- Rate limits are per-second. Sending 50 requests in a burst within 100ms still counts as 50 RPS.
- Batch requests count as 1 request for rate limiting but each method is billed individually.
- Check if you have multiple clients sharing the same keypair.
WebSocket keeps disconnecting
- Check your credit balance โ connections are closed below 10 credits
- Session tokens expire after 1 hour โ implement token renewal
- Network issues โ implement reconnection with exponential backoff
- Subscription volume too high โ reduce subscription count or reconnect after
Retry-After
Service-gate error for archive methods
The hot-node endpoint is optimized for low-latency bots. Archive-only methods such as getEnrichedTransaction, explainTransaction, and getConfirmedTransaction are enabled on dedicated endpoint profiles. When a profile is disabled, the RPC response uses -32004 and no credits are consumed. Methods like getTransaction and getSignaturesForAddress are served by the hot node directly; if the transaction is too old, the upstream node returns its own descriptive error.
Requests timing out
Timeouts vary by method category:
- READ_CHEAP: 5 seconds (e.g.,
getSlot,getBalance) - READ_MEDIUM: 15 seconds (e.g.,
getTransaction) - READ_HEAVY / ARCHIVE: 30 seconds when enabled on dedicated endpoint profiles
- SEND / SEND_BUNDLE: 10 seconds
If requests consistently time out, the Solana node may be under load โ check /health.