Skip to main content
Errors follow a uniform JSON shape:
{
  "error": "code_snake_case",
  "message": "Human-readable explanation",
  "details": { "field": "additional context, optional" }
}
The HTTP status code carries the broad category; the error field carries the precise reason.

400 Bad Request

CodeMeaningFix
invalid_emailEmail format invalidSend a real user@host.tld
invalid_amountTop-up amount outside [$1, $5000]Stay in range
weak_keySSH key strength below RSA 2048 / Ed25519Use ssh-keygen -t ed25519
duplicate_keyThis public key is already on fileUse the existing one or delete first
invalid_countGPU count not 1/2/4/8Pick one of the four
model_unavailableNo provider has this exact spec right nowTry a different config or wait
billing_disabledStripe not configured (mock mode)Use real backend
validation_failedGeneric schema validationRead details for specifics

401 Unauthorized

CodeMeaningFix
unauthorizedNo session cookie or session expiredSign back in
code_invalidOTP code wrong or expiredRequest a new code
code_expiredCode older than 10 minutesRequest a new code
oauth_state_mismatchOAuth callback state didn’t matchRestart the OAuth flow

403 Forbidden

CodeMeaningFix
forbiddenAuthenticated but not allowed to access this resourceCheck resource ownership
insufficient_balanceWallet < cost of operationTop up

404 Not Found

CodeMeaningFix
not_foundPath / resource doesn’t existCheck the URL / ID
rental_not_foundNo rental with that ID owned by youCheck the ID
key_not_foundSSH key ID doesn’t belong to youCheck the ID

409 Conflict

CodeMeaningFix
already_stoppedRental is already in Stopped stateNo action needed
card_already_on_fileTrying to add a card when one existsUpdate or remove first

429 Too Many Requests

CodeMeaningFix
rate_limitedOver 100 req/min on this sessionBack off, retry after Retry-After header
code_request_rate_limitedMore than 1 OTP request per 30sWait

500 Internal Server Error

CodeMeaningFix
internal_errorSomething we didn’t anticipateRetry once, then file a ticket if it persists
provider_errorUpstream provider failedUsually transient; retry. Persistent → ticket

503 Service Unavailable

CodeMeaningFix
provider_pool_emptyNo upstream provider has the requested GPU right nowWait 30 seconds or try a different family
maintenancePlanned maintenanceCheck status.gpuoutlet.ai

How to handle errors in client code

TypeScript example
async function topUp(amountCents: number) {
  const res = await fetch('/v1/billing/checkout', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ amountCents }),
    credentials: 'include',
  });

  if (res.ok) return res.json();

  const err = await res.json().catch(() => ({ error: 'parse_error' }));

  switch (err.error) {
    case 'unauthorized':
      window.location.href = '/login';
      return;
    case 'invalid_amount':
      throw new Error('Pick an amount between $1 and $5,000.');
    case 'rate_limited':
      const wait = parseInt(res.headers.get('Retry-After') ?? '60', 10);
      throw new Error(`Too fast — wait ${wait}s.`);
    default:
      throw new Error(err.message ?? 'Top-up failed. Try again.');
  }
}

Reporting bugs

If you hit an internal_error that reproduces consistently, send the x-request-id header value (in the response) to help@gpuoutlet.ai — we can look up the exact log trace from that.