Error Codes

Error Codes

All API errors follow a consistent format:

{
  "error": {
    "type": "validation_error",
    "message": "description is required",
    "request_id": "req_01JQ...",
    "details": [
      { "field": "description", "message": "required" }
    ]
  }
}

Error Types

TypeHTTP StatusDescription
validation_error400Request body or parameters are invalid
authentication_error401Missing or invalid API key
authorization_error403Key lacks required permissions
not_found404Resource does not exist
conflict409Resource state conflict (e.g., cancelling a completed job)
idempotency_error409Idempotency key reused with different parameters
rate_limit_exceeded429Too many requests — check Retry-After header
insufficient_credits402Not enough credits to post the job
internal_error500Unexpected server error

Handling Errors

Retryable Errors

These errors are safe to retry with exponential backoff:

  • 429 rate_limit_exceeded — Wait for Retry-After header duration
  • 500 internal_error — Retry up to 3 times
  • 502, 503, 504 — Retry up to 3 times

Non-Retryable Errors

These require fixing the request:

  • 400 validation_error — Fix request body
  • 401 authentication_error — Check API key
  • 402 insufficient_credits — Purchase more credits
  • 404 not_found — Check resource ID
  • 409 conflict — Check resource state

SDK Error Handling

Both SDKs automatically retry retryable errors with exponential backoff.

import { FirstHandError } from '@firsthand/sdk';
 
try {
  await client.createJob({...});
} catch (e) {
  if (e instanceof FirstHandError) {
    console.log(e.type);       // "validation_error"
    console.log(e.status);     // 400
    console.log(e.requestId);  // "req_01JQ..."
    console.log(e.retryable);  // false
  }
}
from firsthand import FirstHandError
 
try:
    client.create_job({...})
except FirstHandError as e:
    print(e.type)        # "validation_error"
    print(e.status)      # 400
    print(e.request_id)  # "req_01JQ..."
    print(e.retryable)   # False