Skip to content

Error Handling

All errors return a consistent JSON structure:

{
"error": "Human-readable description of what went wrong",
"error_code": "machine_readable_code"
}

The HTTP status code indicates the error category. The error_code field gives you a stable string to match against in your code.

Statuserror_codeMeaningFix
401unauthorizedMissing or invalid API keyCheck the X-API-Key header is present and the key is valid
403permission_deniedNo access to this resourceVerify your account has access to the workspace or agent
404not_foundResource does not existVerify the ID in your request path
404spreadsheet_not_foundSheet does not exist or you lack accessCheck the sheet_id and agent_id parameters
422agent_not_foundInvalid agent_id parameterList your agents via GET /v1/me and use a valid ID
422usable_gen_credit_requiredNo active GEN credits on your accountPurchase GEN credits at gen.pro
422insufficient_credits_for_jobNot enough credits to run this generationTop up your credit balance
422job_cannot_be_stoppedGeneration already completed or failedCheck the generation status before attempting to stop

Not all errors should be retried.

Do not retry:

  • 401 Unauthorized — your key is wrong; fix it.
  • 403 Forbidden — you lack permission; retrying won’t help.
  • 404 Not Found — the resource doesn’t exist.
  • 422 Unprocessable Entity — the request is invalid; fix the input.

Retry with backoff:

  • 429 Too Many Requests — you hit a rate limit. Wait and retry with exponential backoff.
  • 500 / 502 / 503 — server-side issue. Retry with exponential backoff, up to a reasonable limit.
const BASE_URL = "https://api.gen.pro/v1";
async function genRequest(path: string, options: RequestInit = {}) {
const url = `${BASE_URL}${path}`;
const response = await fetch(url, {
...options,
headers: {
"X-API-Key": process.env.GEN_API_KEY!,
"Content-Type": "application/json",
...options.headers,
},
});
if (!response.ok) {
const body = await response.json().catch(() => ({}));
const errorCode = body.error_code || "unknown";
const message = body.error || response.statusText;
// Don't retry client errors
if (response.status >= 400 && response.status < 500) {
throw new Error(`[${errorCode}] ${message}`);
}
// Server errors — caller can retry
throw new RetryableError(`[${errorCode}] ${message}`, response.status);
}
return response.json();
}
class RetryableError extends Error {
constructor(message: string, public status: number) {
super(message);
this.name = "RetryableError";
}
}
// Usage with retry
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err) {
if (!(err instanceof RetryableError) || attempt === maxRetries) {
throw err;
}
const delay = Math.min(1000 * 2 ** attempt, 30000);
await new Promise((r) => setTimeout(r, delay));
}
}
throw new Error("Unreachable");
}
// Example
const sheet = await withRetry(() =>
genRequest(`/autocontentengine/sheet_123?agent_id=agent_456`)
);