Error Codes
Every Balchemy API error returns a predictable JSON envelope with a status code, message, and optional code field. This page documents the error format, all relevant HTTP status codes, and common error scenarios by domain.
Error envelope format
REST API errors
All REST errors return a consistent JSON body:
{
"statusCode": 401,
"message": "Unauthorized",
"error": "Unauthorized"
}For validation errors, the message field is an array of strings:
{
"statusCode": 400,
"message": ["message must be a string", "message should not be empty"],
"error": "Bad Request"
}For domain-specific service exceptions, an additional code field is included:
{
"statusCode": 503,
"message": "LLM provider unavailable",
"code": "LLM_PROVIDER_UNAVAILABLE",
"error": "Service Unavailable"
}MCP (JSON-RPC) errors
MCP tool calls use the JSON-RPC 2.0 error format:
{
"jsonrpc": "2.0",
"id": "req-uuid",
"error": {
"code": -32603,
"message": "Tool execution failed: insufficient balance",
"data": {
"tool": "trading_solana_jupiter_swap",
"reason": "INSUFFICIENT_BALANCE"
}
}
}Standard JSON-RPC error codes used by the MCP layer:
| JSON-RPC code | Meaning |
|---|---|
-32700 | Parse error — malformed JSON body |
-32600 | Invalid request — missing jsonrpc, method, or id |
-32601 | Method not found — tool does not exist or is not exposed |
-32602 | Invalid params — required parameter missing or wrong type |
-32603 | Internal error — tool execution failed |
HTTP status code reference
| Code | Name | When it occurs |
|---|---|---|
200 | OK | Request succeeded. Response body contains data. |
201 | Created | Resource created successfully (e.g. new bot, new API key). |
400 | Bad Request | Invalid request body, missing required field, or parameter validation failure. |
401 | Unauthorized | Missing Authorization header, expired token, invalid nonce, or wallet mismatch. |
403 | Forbidden | Valid token but insufficient scope, missing CSRF token, or domain validation failure. |
404 | Not Found | Resource does not exist or the authenticated user does not have access to it. |
409 | Conflict | Duplicate resource (e.g. agent already claimed, wallet already connected). |
413 | Content Too Large | Request body or LLM token limit exceeded. |
422 | Unprocessable Entity | Semantic validation failed — body is valid JSON but fields are logically invalid. |
429 | Too Many Requests | Rate limit exceeded. Includes X-RateLimit-* headers. |
500 | Internal Server Error | Unhandled exception in the server. Check Sentry for trace ID. |
503 | Service Unavailable | Trading engine down, LLM provider unreachable, or circuit breaker open. |
Auth errors
| Scenario | Status | Message | Resolution |
|---|---|---|---|
No Authorization header | 401 | Unauthorized | Include Authorization: Bearer <token> on every authenticated request. |
| Expired session token | 401 | Token expired | Call POST /api/nest/auth/refresh with your refresh token. |
| Invalid nonce | 401 | Invalid or expired nonce | Request a fresh nonce with POST /api/nest/auth/nonce or POST /api/nest/auth/evm/nonce. |
| Nonce already used | 401 | Nonce already consumed | Each nonce is single-use. Request a fresh one for each login attempt. |
| Wallet address mismatch | 401 | Wallet address does not match signed message | The address in the request body must match the wallet that signed the SIWE message. |
| CSRF token missing | 403 | Forbidden | Include X-CSRF-Token on all POST, PATCH, PUT, and DELETE requests. Fetch a token at GET /api/nest/auth/csrf. |
| CSRF token invalid | 403 | Invalid CSRF token | CSRF tokens are bound to sessions. Fetch a fresh token after each login. |
| Insufficient scope (MCP) | 403 | Scope insufficient: required trade, got read | Onboard with the required scope or request a step-up token. See Hub — Scopes. |
| Revoked token | 401 | Token has been revoked | The identity access token was revoked via POST /api/public/erc8004/onboarding/tokens/revoke. Re-onboard to get a new token. |
Trading errors
| Scenario | Status | Message | Resolution |
|---|---|---|---|
| Trading disabled on bot | Soft error (200) | "Trading bu bot icin kapali." | Enable trading in Studio bot settings. |
| Trading service unavailable | Soft error (200) | "Trading servisi su anda kullanilamiyor." | The Rust trading engine is not reachable. Wait and retry. |
| Insufficient balance | 400 | Insufficient balance for requested swap amount | Reduce the swap amount or fund the custodial wallet. |
| Slippage exceeded | 400 | Slippage tolerance exceeded | Increase slippageBps or reduce the trade size. |
| Token not found | 404 | Token not found: <mint> | Verify the mint address or contract address. Use trading_solana_jupiter_tokens_search to resolve. |
| Circuit breaker open | 503 | Circuit breaker open for provider: jupiter | The trading engine has tripped the circuit breaker on a provider due to repeated failures. Wait 60 seconds and retry. |
| Order approval required | 200 (pending) | Order created with status: "pending_approval" | Call trading_orders_approve with the returned order_id, or set autoApprove=true in your order profile. |
| Input mint constraint (Solana) | 400 | inputMint must be native SOL or USDC (P3 constraint) | Swap only SOL or USDC as the input token on Solana. |
| Custodial wallet not provisioned | 400 | Custodial wallet required for this operation | Call trading_wallet_custodial_ensure first to provision a custodial wallet. |
| Kill switch active | 503 | Trading kill switch is active for chain: solana | A platform-level kill switch is active. Trading is temporarily halted. |
MCP errors
| Scenario | Status / Code | Message | Resolution |
|---|---|---|---|
| Invalid MCP API key | 401 | Unauthorized | The MCP key is missing, expired, or revoked. Rotate keys at POST /api/nest/bots/:botId/mcp/keys. |
| Tool not found (default mode) | JSON-RPC -32601 | Method not found | The tool is not exposed in default mode. Enable MCP_EXPOSE_GRANULAR_TOOLS or use an agent-level tool instead. |
| Tool not found (granular mode) | JSON-RPC -32601 | Tool not found: <name> | The tool name is misspelled or does not exist in the catalog. Check the Tool Catalog. |
| Missing required parameter | JSON-RPC -32602 | INVALID_PARAMS: <field> is required | Include all required parameters documented for the tool. |
| Step-up required for manage scope | 403 | Step-up token required for manage scope | Issue a step-up token via POST /api/nest/bots/:botId/mcp/step-up and include it as X-Step-Up-Token. |
| Bot not found | 404 | Bot not found | The publicId in the MCP endpoint URL does not match any bot. |
| MCP payload too large | 400 | Request entity too large | Reduce the size of the arguments object in the tools/call payload. |
Agent (ERC-8004) errors
| Scenario | Status | Message | Resolution |
|---|---|---|---|
| Agent already claimed | 409 | Agent already claimed | Each agentId can only be claimed once. Use the existing binding or choose a different agentId. |
| Invalid identity token | 401 | Identity token validation failed | The ES256 JWT is malformed, expired, or signed with an unregistered key. Check token expiry and provider registration. |
| Unknown identity provider | 401 | Unknown identity provider: <provider> | The provider value is not registered. Contact Balchemy to register your provider. |
| Scope denied at onboarding | 403 | Requested scope not available | manage scope is never issued at onboarding time. Start with read or trade. |
| Token JTI already revoked | 400 | Token already revoked | The token was previously revoked. No action needed — the token is already invalid. |
Service exception codes
These codes appear in the code field for server-side service exceptions:
| Code | HTTP Status | Description |
|---|---|---|
AUTHENTICATION_FAILED | 401 | Session authentication failed. |
AUTHORIZATION_FAILED | 403 | Valid session but operation not permitted. |
TOKEN_VALIDATION_FAILED | 401 | JWT or API key validation error. |
INVALID_CREDENTIALS | 401 | Signature verification failed. |
VALIDATION_ERROR | 400 | Input validation failed. |
RESOURCE_NOT_FOUND | 404 | Requested resource does not exist. |
RESOURCE_CONFLICT | 409 | Operation would create a duplicate resource. |
INTERNAL_SERVICE_ERROR | 500 | Unhandled internal exception. |
LLM_PROVIDER_NOT_FOUND | 404 | No LLM provider configured for this bot. |
LLM_PROVIDER_UNAVAILABLE | 503 | LLM provider returned an error or is unreachable. |
LLM_CIRCUIT_BREAKER_OPEN | 503 | LLM circuit breaker is open due to repeated failures. |
LLM_CONFIGURATION_ERROR | 500 | Misconfigured LLM provider settings. |
LLM_RATE_LIMIT_EXCEEDED | 429 | LLM provider rate limit reached. |
LLM_TOKEN_LIMIT_EXCEEDED | 413 | Prompt exceeded the LLM context window. |
SDK error codes
The Agent SDK maps API errors to typed AgentSdkError codes:
| SDK code | Trigger |
|---|---|
auth_error | 401 response from the API |
policy_error | 403 response (scope or CSRF) |
rate_limit_error | 429 response |
provider_auth_error | Identity provider validation failure |
network_error | Network timeout or connection refused |
execution_error | Tool execution returned an error result |
invalid_response | Empty or non-JSON-RPC response body |
unknown_error | Unclassified error |
Catch and inspect the code in your error handler:
import { AgentSdkError } from "@balchemy/agent-sdk";
try {
const result = await mcp.agentExecute({ instruction: "..." });
} catch (err: unknown) {
if (err instanceof AgentSdkError) {
switch (err.code) {
case "auth_error":
// Re-onboard or refresh your MCP key
break;
case "rate_limit_error":
// Back off — Retry-After header value is in err.details
break;
case "network_error":
// Retry with exponential backoff
break;
}
}
}The SDK implements automatic retry with exponential backoff for network_error and rate_limit_error. It does not retry execution_error (tool logic errors are deterministic).