Skip to main content

Error Classes

JwtExpiredError

Thrown by execute() when the OAuth JWT has expired and the session is not yet registered on-chain. The user must re-authenticate.
import { JwtExpiredError } from '@cavos/react';

try {
  await execute(calls);
} catch (err) {
  if (err instanceof JwtExpiredError) {
    // Prompt re-login
    await login('google');
  }
}
JWTs from Google/Apple typically expire after 1 hour. Once the session key is registered on-chain (first transaction), subsequent transactions use the session key directly — no JWT needed until the session itself expires.

Authentication Errors

OAuth Login Cancelled or Blocked

try {
  await login('google');
} catch (error) {
  if (error.message.includes('closed without completing')) {
    showMessage('Login window was closed. Please try again.');
  } else if (error.message.includes('timed out')) {
    showMessage('Login timed out. Please try again.');
  }
}

Nonce Mismatch

Happens if session state was corrupted between the OAuth redirect and the callback:
try {
  await login('google');
} catch (error) {
  if (error.message.includes('nonce')) {
    // Clear any stale state and retry
    sessionStorage.clear();
    await login('google');
  }
}

Session Errors

Session Not Registered

Thrown when execute() is called with gasless: false before any on-chain session exists:
try {
  await execute(calls, { gasless: false });
} catch (error) {
  if (error.message.includes('non-sponsored transaction without a registered session')) {
    // Execute one sponsored tx first — it registers the session automatically
    await execute(calls); // gasless: true (default)
    // Now retry with user-pays
  }
}

Session Expired (Beyond Grace Period)

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('SESSION_EXPIRED')) {
    // Session expired beyond the 48h grace window — must re-login
    await login('google');
  }
}

Session Expired (Within Grace Period)

The SDK handles this automatically — execute() will call renewSession() internally before proceeding. You don’t need to handle this case explicitly.

Wallet Not Initialized

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('not initialized') || error.message.includes('Please login')) {
    await login('google');
  }
}

Transaction Errors

Session Policy Violation

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('Spending limit exceeded')) {
    showMessage('This transaction exceeds your session spending limit.');
  } else if (error.message.includes('not in allowedContracts') || error.message.includes('policy')) {
    showMessage('This contract is not permitted by your session policy.');
  }
}

Paymaster Error

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('paymaster') || error.message.includes('gas tank')) {
    showMessage('Gasless sponsorship is temporarily unavailable. Try again shortly.');
  }
}

Insufficient Balance (User-Pays)

try {
  await execute(calls, { gasless: false });
} catch (error) {
  if (error.message.includes('insufficient') || error.message.includes('max_fee')) {
    showMessage('Insufficient STRK balance for transaction fee.');
  }
}

Contract / Entrypoint Not Found

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('Contract not found')) {
    showMessage('Contract does not exist on this network.');
  } else if (error.message.includes('Entry point not found')) {
    showMessage('Function does not exist on this contract.');
  }
}

Wallet Errors

Account Not Deployed

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('not deployed')) {
    // Deployment normally happens automatically — trigger manually if needed
    await deployAccount();
    await execute(calls);
  }
}

Address Seed Mismatch

Happens when the identity (sub) or app salt doesn’t match what the wallet was created with:
try {
  await login('google');
} catch (error) {
  if (error.message.includes('Address seed mismatch')) {
    // Identity verification failed — clear state and re-authenticate
    await logout();
    await login('google');
  }
}

MAU Limit Exceeded

try {
  await login('google');
} catch (error) {
  if (error.message.includes('MAU limit')) {
    showMessage('App has reached its monthly active user limit.');
  }
}

Network Errors

RPC Timeout or Rate Limit

try {
  await execute(calls);
} catch (error) {
  if (error.message.includes('timeout') || error.message.includes('network')) {
    showMessage('Network error. Please check your connection and try again.');
  }
}
To avoid rate limits on the default shared RPC, provide your own:
<CavosProvider config={{
  appId: 'your-app-id',
  starknetRpcUrl: 'https://your-own-rpc-node.com',
}}>

Debugging

Enable Logging

<CavosProvider config={{
  appId: 'your-app-id',
  enableLogging: true,
}}>

Inspect Wallet State

const { isAuthenticated, address, walletStatus, sessionPublicKey } = useCavos();

console.log({
  isAuthenticated,
  address,
  sessionPublicKey,
  walletStatus, // { isDeploying, isDeployed, isRegistering, isSessionActive, isReady, pendingDeployTxHash? }
});

Error Recovery Reference

ErrorCauseRecovery
JwtExpiredErrorOAuth JWT expired before session was registeredRe-login via login()
SESSION_EXPIREDSession expired past 48h grace windowRe-login via login()
non-sponsored transaction without a registered sessiongasless: false before first txExecute a sponsored tx first
Spending limit exceededTransaction exceeds session policy limitCheck spendingLimits in policy
Address seed mismatchIdentity/salt mismatchLogout and re-login
MAU limitApp exceeded monthly usersUpgrade plan or wait for reset
paymaster / gas tankPaymaster temporarily unavailableRetry, or use gasless: false
not initializedexecute() called before login()Call login() first
timeoutRPC timeoutRetry, or configure a custom RPC URL