Skip to content

Error Reference

Complete error code reference for all JarvisClaw API services. Includes HTTP status codes, x402 payment errors, and service-specific error codes.

Base URL: https://api.jarvisclaw.ai/v1

HTTP Status Codes

CodeNameDescriptionResolution
400Bad RequestMalformed request body or missing required parameters.Check request format and required fields.
401UnauthorizedInvalid or missing API key.Verify your API key is correct and active.
402Payment Requiredx402 payment needed or insufficient USDC balance.Top up USDC wallet or check payment signature.
403ForbiddenAPI key lacks permission for this resource.Check key permissions in dashboard.
404Not FoundEndpoint or resource does not exist.Verify the URL path is correct.
429Rate LimitedToo many requests in time window.Back off and retry after the Retry-After header value.
500Internal Server ErrorUnexpected server failure.Retry with exponential backoff; contact support if persistent.
502Bad GatewayUpstream provider unavailable.Retry; the provider may be temporarily down.
503Service UnavailableService overloaded or in maintenance.Wait and retry after a few seconds.

Error Body Shapes

Two shapes are in circulation, so handle both.

OpenAI-compatible relay endpoints (/v1/chat/completions, /v1/messages, /v1/images/*, /v1/audio/*, …):

json
{ "error": { "message": "...", "type": "...", "code": "..." } }

AIP, wallet, and marketplace endpoints:

json
{ "error": "human-readable message" }

Both SDKs normalise this — Python's APIError.message and Go's APIError.Message read whichever form arrived, with the raw body on .body / .Body.

Don't branch on error code strings

Only the relay path emits a code, and the values are whatever the upstream provider returned. The AIP and wallet surfaces return a bare message string with no code at all. Branch on HTTP status and error type; treat any code as diagnostic text.

x402 Payment Failures

A payment problem surfaces as 402 with the x402 challenge body, or as a 402/502 carrying the facilitator's rejection message. Common causes:

CauseSymptomResolution
Insufficient USDC402 on the paid retryTop up your wallet (Base or Solana)
Signature mismatchFacilitator verification failsConfirm the private key controls the from address
Nonce reuseReplay rejectionSDKs generate a fresh random nonce per attempt
Authorization expiredRejected after maxTimeoutSeconds (300s)Re-sign and retry; SDKs do this automatically
Amount mismatchRejected on verificationSign exactly the amount from the 402; do not round
Unsupported networkNo usable option in acceptsUse Base (eip155:8453) or Solana mainnet
Missing Solana ATASolana path skippedReceive one USDC transfer to initialise the token account
Price unavailable503 price_unavailableUpstream price probe failed; retry shortly

Solana is only advertised when the gateway knows a fee payer — if it doesn't, accepts lists Base alone. See Agent Payments (x402).

Common Failure Modes

SituationStatusNotes
Unknown or retired model400Check GET /v1/models; smart-route aliases need the auto/ prefix
Context window exceeded400Trim history or move to a larger-context model
Content filter400 / job failedAsync jobs report status: "failed" with retryable: false
Upstream provider error502Retry, or pin a different model
Concurrency cap hit429Marketplace services cap concurrent requests
Async job still running200 with status: "in_progress"Keep polling until completed or failed

Code Examples

bash
# Check HTTP status and error body
curl -s -w "\nHTTP_STATUS:%{http_code}" \
  https://api.jarvisclaw.ai/v1/chat/completions \
  -H "Authorization: Bearer $JARVISCLAW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello"}]}' \
| tee >(tail -1 | grep -o "HTTP_STATUS:[0-9]*") \
| head -n -1 | jq .

# On error the body will contain:
# { "error": { "code": "invalid_model", "message": "..." } }
# or for x402:
# { "error": { "code": "insufficient_balance", "message": "..." } }
python
import time

from jarvisclaw import (
    OpenAI,
    APIError,
    AuthenticationError,
    InsufficientBalanceError,
    JarvisClawError,
    RateLimitError,
)

# OpenAI is the drop-in client; ChatClient / JarvisClaw work the same way.
client = OpenAI(api_key="sk-your-api-key")

def chat_with_retry(messages, model="openai/gpt-4o-mini", max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.chat.completions.create(
                model=model,
                messages=messages,
            )

        except AuthenticationError as e:
            # 401 — bad API key, no point retrying
            print(f"Authentication failed: {e.message}")
            raise

        except InsufficientBalanceError as e:
            # 402 — out of USDC; top up and retry manually
            print(f"Insufficient balance: {e.message}")
            print(f"Error code: {e.body.get('error', {}).get('code')}")
            raise

        except RateLimitError as e:
            # 429 — back off using Retry-After when available
            wait = e.retry_after or (2 ** attempt)
            print(f"Rate limited. Retrying in {wait}s …")
            time.sleep(wait)

        except APIError as e:
            # 500 / 502 / 503 — transient server errors
            if e.status_code in (500, 502, 503) and attempt < max_retries - 1:
                wait = 2 ** attempt
                print(f"Server error {e.status_code}. Retrying in {wait}s …")
                time.sleep(wait)
            else:
                # 400 / 403 / 404 or exhausted retries
                code = e.body.get("error", {}).get("code", "unknown")
                print(f"API error [{e.status_code}] {code}: {e.message}")
                raise

        except JarvisClawError as e:
            # x402 payment / SDK-level errors
            print(f"SDK error: {e}")
            raise

    raise RuntimeError("Max retries exceeded")
go
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	jarvisclaw "github.com/api-jarvisclaw/go-sdk/v2"
)

func chatWithRetry(
	ctx context.Context,
	client *jarvisclaw.Client,
	messages []jarvisclaw.Message,
	maxRetries int,
) (*jarvisclaw.ChatResponse, error) {
	for attempt := range maxRetries {
		resp, err := client.ChatCompletion(ctx, "openai/gpt-4o-mini", messages)
		if err == nil {
			return resp, nil
		}

		var authErr *jarvisclaw.AuthenticationError
		if errors.As(err, &authErr) {
			// 401 — invalid API key, no retry
			return nil, fmt.Errorf("authentication failed: %w", err)
		}

		var balanceErr *jarvisclaw.InsufficientBalanceError
		if errors.As(err, &balanceErr) {
			// 402 — top up USDC wallet
			return nil, fmt.Errorf("insufficient balance: %w", err)
		}

		var rateErr *jarvisclaw.RateLimitError
		if errors.As(err, &rateErr) {
			// 429 — back off
			wait := time.Duration(1<<attempt) * time.Second
			fmt.Printf("Rate limited. Retrying in %s ...\n", wait)
			time.Sleep(wait)
			continue
		}

		var apiErr *jarvisclaw.APIError
		if errors.As(err, &apiErr) {
			switch apiErr.StatusCode {
			case 500, 502, 503:
				// Transient server error — exponential backoff
				if attempt < maxRetries-1 {
					wait := time.Duration(1<<attempt) * time.Second
					fmt.Printf("Server error %d. Retrying in %s ...\n", apiErr.StatusCode, wait)
					time.Sleep(wait)
					continue
				}
			default:
				// 400 / 403 / 404 / upstream_error — not retryable
				code := apiErr.Body["error"]
				return nil, fmt.Errorf("api error [%d] %v: %s", apiErr.StatusCode, code, apiErr.Message)
			}
		}

		// PaymentError / other SDK errors
		return nil, fmt.Errorf("sdk error: %w", err)
	}
	return nil, fmt.Errorf("max retries (%d) exceeded", maxRetries)
}

Error types carry no Type field

Go's APIError exposes StatusCode, Message, and Body — switch on the concrete error type or the status code, not a string discriminator. AuthenticationError, RateLimitError, and InsufficientBalanceError embed APIError; PaymentError embeds JarvisClawError. The Python hierarchy mirrors this — see SDK → Error Handling.