Map each error.code returned by the envelope to a concrete recovery action. Use this table when wiring aigateway into an agent prompt or a control-flow layer.
error.code |
Exit | Recommended Recovery |
|---|---|---|
WALLET_NOT_CONFIGURED |
1 | Run aigateway wallet-init once (auto-creates a local session wallet). |
SERVICE_URL_MISSING |
1 | Override via env AIGATEWAY_SERVICE_URL. The default service URL is wired into the CLI for production; this should never trigger in normal use. |
AMOUNT_INVALID |
1 | Caller bug — input must be a positive numeric string (used by wallet-topup, wallet-gas, wallet-withdraw, --topup-amount). |
AMOUNT_EXCEEDS_BALANCE |
1 | wallet-withdraw --amount exceeded available balance of --token. error.token says which asset; use smaller of requested vs. error.available. |
NEEDS_AMOUNT |
1 | Non-TTY wallet-withdraw requires both --amount <n> and --token <symbol> (use the envelope's tokenSymbol/nativeSymbol). Re-run with both. |
INVALID_WITHDRAW_TOKEN |
1 | wallet-withdraw --token must match the wallet's payment or gas token (envelope tokenSymbol/nativeSymbol). (Deprecated alias: INVALID_TOKEN.) |
INSUFFICIENT_BALANCE |
1 | Top-up failed or the chosen --topup-amount was still below the call's required amount. Surface error.required / error.available; ask the user to retry with a larger top-up. (Deprecated alias: INSUFFICIENT_USDT.) |
INSUFFICIENT_GAS |
1 | Approve / withdraw needs the native gas token. Run aigateway wallet-gas (WalletConnect, must be interactive), then retry. (Deprecated alias: INSUFFICIENT_BNB.) |
NO_FUNDS |
1 | Nothing to withdraw. Inform the user; suggest wallet-topup if relevant. |
NO_MAIN_WALLET |
1 | wallet-withdraw invoked with no mainWallet saved. Re-run with --to <address>. |
MISSING_MODEL |
1 | sb invoke --model is required. Run aigateway sb tools to pick one. |
MISSING_INPUTS |
1 | sb invoke --inputs is required, or the JSON is missing required fields. error.errors[] lists which fields are missing; error.required[] lists all required keys for the model. |
INVALID_INPUTS |
1 | Inputs failed schema validation. error.errors[] items carry { field, kind, message } where kind ∈ {enum, type, range}. Fix and retry. |
INVALID_INPUTS_JSON |
1 | --inputs could not be parsed as JSON. Check quoting / escaping; on shells use JSON.stringify(...) from your wrapper. |
INPUTS_FILE_NOT_FOUND |
1 | --inputs @path file does not exist. Resolve to an absolute path or correct the caller. |
INVALID_MODEL_ID |
1 | Server / catalog rejected the model id. Run aigateway sb tools --category <key> to find a valid one. |
CATEGORY_NOT_FOUND |
1 | sb tools --category argument is not in the live catalog. Run aigateway sb tools (no filter) to list categories. |
MODEL_PRICING_NOT_CONFIGURED |
1 | The catalog lists the model but the gateway has no price entry yet. Pick another model or ask the operator to add it. |
INVALID_BODY |
1 | Server rejected the request body shape. Usually a CLI / catalog drift; file a bug. |
TOPUP_REQUIRED |
1 | Non-TTY context, USDT below the per-call minimum. Choose from error.presets (filtered to ≥ error.minTopup) and rerun the failing command with --topup-amount <n>. |
TOPUP_AMOUNT_TOO_SMALL |
1 | --topup-amount (or topup --amount) below error.minTopup. Rerun with a larger value. |
PAYMENT_REJECTED |
1 | User cancelled in their wallet. Do not auto-retry — ask user first. |
PAYMENT_TIMEOUT |
2 | WalletConnect approval window expired (5 min). Ask user whether to retry. Do not auto-retry. |
WC_SESSION_EXPIRED |
2 | WalletConnect relay dropped the session. Re-run the original command. |
TX_TIMEOUT |
2 | The on-chain transfer is likely still pending. Wait, then re-check with wallet-balance. |
UPDATE_APPLIED |
2 | CLI just upgraded itself synchronously. The previous command was not executed. Surface error.from → error.to and rerun the same command verbatim on the new version. |
SERVICE_UNAVAILABLE |
3 | Exponential backoff: 1 s → 4 s → 16 s, max 3 attempts. |
PAYMENT_FETCH_FAILED |
3 | First 402 probe failed. Backoff + retry; check network connectivity. |
CATALOG_FETCH_FAILED |
3 | sb tools could not reach the server. Retry once; if it persists, sb invoke still runs (server-side validation is the safety net). |
BALANCE_CHECK_FAILED |
3 | BSC RPC hiccup. Retry once after 2 s. |
ALLOWANCE_CHECK_FAILED |
3 | BSC RPC hiccup. Retry once after 2 s. |
TX_REVERTED |
3 | On-chain failure. Capture error.message for diagnosis; do not retry blindly. |
WITHDRAW_FAILED |
3 | Withdraw transaction failed. Check aigateway wallet-balance and retry. |
APPROVE_FAILED |
3 | One-time facilitator approve failed during wallet-topup. Inspect the tx; retry the top-up. |
INVALID_PAYMENT_AMOUNT |
3 | Server-side issue (returned amount = 0). Retry after a short delay. |
PAYMENT_FAILED |
3 | Service rejected the signed payment request. Surface error.data / error.status to the user / log. On 5xx, retry once. |
IMAGE_DOWNLOAD_FAILED / DOWNLOAD_FAILED |
3 | The paid call succeeded but the local download failed. The original URL is still in the upstream response — surface it from data.downloaded[].url (when available) or re-fetch via --raw. Do not re-invoke the model (you'd pay again). |
FUNDING_FAILED |
3 | Non-timeout / non-reject failure in the WalletConnect funding flow. Re-run wallet-topup. |
INTERNAL_ERROR |
4 | File a bug. Don't retry. |
WALLET_ERROR |
1 | Generic wallet failure. Surface to user, ask whether to retry. |
async function withRetry(fn, { codes, attempts = 3, baseDelayMs = 1000 } = {}) {
for (let i = 0; i < attempts; i++) {
const { exitCode, envelope } = await fn();
if (envelope.ok) return envelope;
if (!codes.includes(envelope.error.code)) return envelope; // non-retryable
if (i < attempts - 1) await new Promise(r => setTimeout(r, baseDelayMs * 4 ** i));
}
}
// Only retry transient service/network errors
await withRetry(
() => runAigateway([
"sb", "invoke",
"--model", "replicate/black-forest-labs/flux-schnell",
"--inputs", JSON.stringify({ prompt: "a cyberpunk fox" }),
]),
{
codes: [
"SERVICE_UNAVAILABLE",
"PAYMENT_FETCH_FAILED",
"CATALOG_FETCH_FAILED",
"BALANCE_CHECK_FAILED",
"ALLOWANCE_CHECK_FAILED",
"INVALID_PAYMENT_AMOUNT",
],
},
);- ❌ Don't retry
PAYMENT_REJECTEDorPAYMENT_TIMEOUTautomatically — the user actively cancelled or walked away. - ❌ Don't retry
IMAGE_DOWNLOAD_FAILED/DOWNLOAD_FAILEDby re-invoking the model — the paid call already settled, you'd be charged again. Re-fetch the URL or re-run with--raw. - ❌ Don't match on
error.messagetext — messages may change between versions. Match onerror.code. - ❌ Don't ignore exit code in favour of envelope. Stack-level proxies sometimes mangle stdout; the exit code is a redundant safety net.
- ❌ Don't paper over
UPDATE_APPLIEDby treating it as a generic timeout — it's a "new binary; rerun me" signal, not a failure.