Skip to main content

Error handling + retry strategies

Common errors and how to recover.

Tx submission errors

"Nonce stale: tx=X, state=Y"

Your local nonce is behind the chain. Re-fetch:

const acct = await client.chain.getAccount(wallet.address);
const tx = buildTransferTx(..., acct.nonce, ...);

"Insufficient balance"

Caller balance < amount + fee. Check balance first:

const acct = await client.chain.getAccount(wallet.address);
if (BigInt(acct.balance) < amount + fee) {
throw new Error("not enough OMBRA");
}

"Signature invalid"

Causes (in order of likelihood):

  1. Canonical JSON serializer reorders fields → use the SDK builders, don't roll your own
  2. privateKey doesn't match from address
  3. Signed wrong bytes — must be utf8(hexHash), not raw 32 bytes

"Tx already in mempool" (409)

Duplicate submission. Either:

  • Your tx was already accepted — check /api/mempool/ or wait for inclusion
  • Different tx with same hash — impossible unless you replayed

"Fork not active"

You submitted a tx type that requires a fork not yet activated on the network:

const { height } = await client.chain.getStats();
if (height < FORK_V6_HEIGHT) {
throw new Error("Fork V6 not active yet, cannot submit AGENT_REQUEST");
}

HTTP errors

429 Too Many Requests

Rate limit. Backoff:

async function withRetry<T>(fn: () => Promise<T>, attempts = 5): Promise<T> {
let delay = 1_000;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err: any) {
if (err.status === 429 && i < attempts - 1) {
await new Promise((r) => setTimeout(r, delay));
delay *= 2;
continue;
}
throw err;
}
}
throw new Error("unreachable");
}

Better: register an API key for higher RPM (Tier 1 = 600 RPM).

503 Service Unavailable

Node is syncing or restarting. Retry with backoff. Typical recovery: < 30s.

5xx server errors

Transient. Retry with exponential backoff. After 3 retries (over ~15s), surface to user.

Network errors

try {
await fetch(url);
} catch (err) {
if (err.message.includes("ECONNREFUSED")) {
// Endpoint down
} else if (err.message.includes("timeout")) {
// Network slow
} else if (err.message.includes("DNS")) {
// DNS failure
}
}

For long-lived clients, implement a circuit breaker:

let failureCount = 0;
let circuitOpen = false;
let openedAt = 0;

async function callApi(url: string) {
if (circuitOpen && Date.now() - openedAt < 30_000) {
throw new Error("circuit open — API down");
}
try {
const res = await fetch(url);
failureCount = 0;
circuitOpen = false;
return res;
} catch (err) {
failureCount++;
if (failureCount > 5) {
circuitOpen = true;
openedAt = Date.now();
}
throw err;
}
}

Task lifecycle errors

Task stays "submitted" forever

No miner picked it up. Causes:

  • Fee too low (no miner found it profitable)
  • taskType not supported by any online miner
  • Network has no miners online (rare on mainnet)

After timeoutMs, the task transitions to "timeout". Refund mechanism: not yet implemented — the fee is forfeited to chain burn.

Task "validated" but never "rewarded"

Bug in validator that produced TASK_VALIDATE without follow-up TASK_REWARD. Should not happen — validators are designed to emit both atomically. If it does, file a bug report.

Agent run "failed"

The claimed miner aborted. Common reasons:

  • LLM produced malformed output (parsing error)
  • Tool call failed unexpectedly (network, sandbox crash)
  • Miner ran out of memory

Look at /api/agent/v6/run/:id/steps to see what step failed.

Idempotency

Submitting the same tx twice is safe — the chain rejects the duplicate with 409. Don't retry blindly on success — only on network/server errors.

async function submitOnce(tx: any) {
try {
return await client.chain.submitTx(tx);
} catch (err: any) {
if (err.message?.includes("already in mempool") || err.status === 409) {
return { ok: true, hash: tx.hash }; // treat as success
}
throw err;
}
}

Logging recommendations

Log every tx with:

  • hash
  • type
  • from + nonce
  • Submit timestamp
  • API response (status, body)

This lets you trace any "where did my tx go" question via mempool + chain queries.

When to give up

Network outages or chain halts are rare but possible. After:

  • 5 consecutive 5xx errors
  • 5 min in submitted status with no inclusion

Surface the error to the user and stop retrying. Hammering won't help.