Обработка ошибок
Формат ответа об ошибке
Все ошибки API следуют единой JSON-структуре с соответствующим HTTP-кодом статуса:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description of what went wrong"
}
}Поле code -- это стабильный, машиночитаемый идентификатор, который можно использовать в логике обработки ошибок. Поле message предоставляет дополнительный контекст и может варьироваться между различными случаями одного и того же кода ошибки.
Категории ошибок
Ошибки аутентификации (401)
Возвращаются, когда запрос не может быть аутентифицирован.
| Код | Описание |
|---|---|
UNAUTHORIZED | Заголовок Authorization отсутствует, имеет неправильный формат или содержит недействительный токен. |
KEY_EXPIRED | API-ключ был действителен, но его срок действия истек. Создайте новый ключ для продолжения. |
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid Authorization header"
}
}Ошибки авторизации (403)
Возвращаются, когда аутентифицированный пользователь не имеет разрешения на доступ к запрашиваемому ресурсу.
| Код | Описание |
|---|---|
FORBIDDEN | Вы пытаетесь получить доступ к ресурсу, принадлежащему другому пользователю, или изменить его (например, отозвать чужой API-ключ). |
Ошибки "Не найдено" (404)
Возвращаются, когда запрашиваемый ресурс не существует.
| Код | Описание |
|---|---|
NOT_FOUND | Ресурс, указанный в URL (извлечение, API-ключ и т.д.), не существует или был удален. |
Ошибки валидации (400)
Возвращаются, когда тело запроса или параметры недействительны.
| Код | Описание |
|---|---|
VALIDATION_ERROR | Тело запроса не прошло валидацию схемы. Поле message описывает, какие поля недействительны. |
MAX_KEYS_REACHED | Вы достигли максимума в 10 активных API-ключей. Отзовите существующий ключ перед созданием нового. |
{
"error": {
"code": "MAX_KEYS_REACHED",
"message": "Maximum of 10 active API keys allowed"
}
}Ошибки ограничения запросов (429)
Возвращаются при превышении лимитов использования.
| Код | Описание |
|---|---|
USAGE_LIMIT_EXCEEDED | Ваш ежемесячный лимит извлечений достигнут. Перейдите на более высокий план или дождитесь следующего периода оплаты. |
{
"error": {
"code": "USAGE_LIMIT_EXCEEDED",
"message": "Monthly extraction limit reached (25/25). Upgrade your plan for more extractions."
}
}Серверные ошибки (500)
Возвращаются при возникновении непредвиденной ситуации на стороне сервера.
| Код | Описание |
|---|---|
INTERNAL_ERROR | Произошла непредвиденная серверная ошибка. Такие ошибки автоматически логируются и расследуются. |
EXTRACTION_FAILED | Конвейер обработки ИИ столкнулся с ошибкой при извлечении данных из вашего документа. Документ может быть поврежден, нечитаем или иметь неподдерживаемый формат. |
Повторяемые ошибки
Не все ошибки следует повторять. Вот разбивка:
| Код | Повторяемая | Действие |
|---|---|---|
INTERNAL_ERROR | Да | Повторите с экспоненциальной задержкой |
EXTRACTION_FAILED | Да | Повторите -- ИИ может выдать другой результат. Если ошибка сохраняется, проверьте качество документа |
USAGE_LIMIT_EXCEEDED | Нет | Перейдите на более высокий план или дождитесь следующего периода оплаты |
UNAUTHORIZED | Нет | Исправьте учетные данные аутентификации |
KEY_EXPIRED | Нет | Создайте новый API-ключ |
FORBIDDEN | Нет | Вы обращаетесь к ресурсу, который вам не принадлежит |
NOT_FOUND | Нет | Ресурс не существует -- проверьте ID |
VALIDATION_ERROR | Нет | Исправьте тело запроса в соответствии с ожидаемой схемой |
MAX_KEYS_REACHED | Нет | Отзовите существующий ключ перед созданием нового |
Стратегия повторных попыток
Для повторяемых ошибок используйте экспоненциальную задержку, чтобы не перегружать сервер. Вот переиспользуемая реализация:
interface ApiResponse<T> {
data?: T;
error?: { code: string; message: string };
}
async function fetchWithRetry<T>(
url: string,
options: RequestInit,
maxRetries = 3,
): Promise<ApiResponse<T>> {
let lastError: Error | null = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
const body = await response.json();
if (response.ok) {
return body as ApiResponse<T>;
}
const errorCode = body?.error?.code;
// Only retry server errors
if (
(errorCode === "INTERNAL_ERROR" || errorCode === "EXTRACTION_FAILED") &&
attempt < maxRetries
) {
const delayMs = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s
console.log(`Retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})...`);
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
// Non-retryable error -- return immediately
return body as ApiResponse<T>;
} catch (err) {
lastError = err as Error;
if (attempt < maxRetries) {
const delayMs = 1000 * Math.pow(2, attempt);
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
}
}
throw lastError ?? new Error("Max retries exceeded");
}Использование:
const result = await fetchWithRetry<ExtractionResult>(
"https://api.docmap.io/v1/extractions/run",
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "tmpl_abc123",
fileName: "invoice.pdf",
pdfBase64: encodedPdf,
mimeType: "application/pdf",
}),
},
);
if (result.error) {
console.error(`Extraction failed: [${result.error.code}] ${result.error.message}`);
} else {
console.log("Extracted data:", result.data);
}Лучшие практики
Всегда проверяйте HTTP-код статуса первым. Статус
2xxозначает успех; все остальное -- ошибка. Не предполагайте наличие поляdataв теле ответа без проверки.Парсите тело ошибки для получения поля
code. Используйтеcode(а неmessage) в логике управления потоком. Сообщения об ошибках могут меняться со временем, но коды ошибок стабильны.Обрабатывайте конкретные коды ошибок. Ветвление по известным кодам, таким как
USAGE_LIMIT_EXCEEDEDилиKEY_EXPIRED, для показа целевых сообщений пользователям или запуска определенных рабочих процессов.Логируйте неизвестные ошибки. Если вы столкнулись с кодом ошибки, который не обрабатываете явно, логируйте полный ответ (код статуса, код ошибки и сообщение) для отладки.
Не повторяйте ошибки 4xx. Ошибки аутентификации, авторизации, валидации и "не найдено" не разрешатся сами по себе. Исправьте основную проблему перед повторной попыткой.
Установите лимит повторных попыток. Никогда не повторяйте бесконечно. Три повторные попытки с экспоненциальной задержкой (1с, 2с, 4с) -- разумное значение по умолчанию для серверных ошибок.
