Auto-recovery with next_action
Every JECP error returns a structured machine-readable hint so agents can recover without humans.
The shape of an error
{
"jecp": "1.0",
"status": "failed",
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "wallet 0 USDC < 0.005"
},
"next_action": {
"type": "topup",
"ui": "https://jecp.dev/topup",
"api": "https://jecp.dev/api/agents/topup",
"hint": "Top up via Stripe."
}
}
Branching with the SDK
import { JecpError } from '@jecpdev/sdk';
try {
await jecp.invoke('a/b', 'c', input);
} catch (e) {
if (e instanceof JecpError) {
switch (e.nextAction?.type) {
case 'topup':
await jecp.topup(20);
break;
case 'register':
await JecpClient.register({ name: 'NewAgent' });
break;
case 'discover': {
const cat = await jecp.catalog();
// pick a real one
break;
}
case 'retry_after':
await sleep(60_000);
break;
case 'increase_mandate':
// bump budget and retry
break;
case 'earn_trust':
// trust tier too low — try a different capability
break;
// ...
}
}
}
Full next_action catalog (v1)
| type | When | Recovery hint |
|---|---|---|
topup | Wallet too low | Open Stripe Checkout via jecp.topup() |
register | Auth failed entirely | Re-register the agent |
increase_mandate | Mandate budget exhausted | Raise budget if operator approves |
refresh_mandate | Mandate expired | Issue a new mandate |
retry_after | Rate limited | Sleep then retry (Retry-After header) |
discover | Capability not found | Query /v1/capabilities |
see_manifest | Action not found | Read manifest for valid actions |
earn_trust | Trust tier too low | Try a lower-trust capability or build history |
try_alternative_provider | Provider unreachable | Use catalog to find another |
upgrade_client | SDK version too old | Update SDK |
Typed error classes
The SDK provides 9 typed subclasses of JecpError: InsufficientBalanceError, InsufficientBudgetError, MandateExpiredError, AuthError, RateLimitError, CapabilityNotFoundError, ActionNotFoundError, InsufficientTrustError, ProviderError.
Each carries .code, .status, .nextAction, .message, and .raw (full server response).
Auto-retry layered on top
The SDK auto-retries transient errors (5xx, 408, 429, network) with exponential backoff. Your catch block only sees terminal failures, so the dispatch above runs after retries are exhausted. The same request_id is preserved across retries so the Hub's idempotency cache prevents double-charging.