APIDOCINSPECT

Ошибки и деградация

Три формы тела ошибки, полный каталог кодов /v1/process, extraction_status и warnings[] — как API сообщает о частичном или ненадежном результате.

API различает два принципиально разных сигнала: «запрос не выполнен» (HTTP-ошибка, тела результата нет) и «запрос выполнен, но результату стоит не полностью доверять» (200 OK, но extraction_status и warnings[] говорят, что часть данных — не более чем лучшая попытка). Второе — не баг, а осознанная деградация: сервис отдает то, что смог, вместо молчаливого падения.

Три формы тела ошибки

Это первое, обо что спотыкается парсер ошибок на клиенте: форм три, и они отличаются по уровню, на котором ошибка поймана.

1. Ошибки формы запроса (валидация /v1/process) — HTTP 422, поле detail — объект:

{
  "detail": {
    "code": "PROMPT_REQUIRES_SCHEMA",
    "message": "prompt без output_schema не поддерживается…",
    "details": {}
  }
}

2. Ошибки обработки (модель, парсинг, NDA-граница, размер файла) — привычный конверт:

{
  "success": false,
  "error": {
    "code": "FILE_TOO_LARGE",
    "message": "File size 62914560 exceeds maximum 52428800 bytes",
    "details": { "size": 62914560, "max_size": 52428800 }
  }
}

3. Ошибка ключа — HTTP 401, detail — обычная строка:

{ "detail": "Invalid API key." }

Ключ проверяется раньше эндпоинта, поэтому формат отличается от обеих предыдущих форм. Подробнее — в разделе «Аутентификация».

Практическое правило разбора: сначала detail?.code, затем error?.code, затем detail как строка.

Каталог кодов

Форма запроса — 422, тело {"detail": {code, …}}

КодКогда
SOURCE_REQUIREDНи file, ни text не переданы
SOURCE_CONFLICTПереданы и file, и text — нужен ровно один источник
EMPTY_TASKЗапрос ничего не просит: нет непустой output_schema, split, match или include
UNKNOWN_FIELDПоле вне контракта (опечатка вроде promt); details.unknown — список, details.allowed — принимаемые поля
UNSUPPORTED_FILE_TYPEРасширение вне списка форматов; details.supported — актуальный список
PROMPT_REQUIRES_SCHEMAprompt без непустой output_schema
PROMPT_TOO_LONGprompt длиннее ~10 000 знаков
INCLUDE_UNKNOWNВ include секция вне markdown/fragments/tables/fields
SPLIT_REQUIRES_PDFsplit указан не для PDF-файла
COMBINATION_NOT_SUPPORTEDСочетание параметров пока не поддерживается (сейчас: split + match)
MATCH_REGISTRY_REQUIREDmatch без файла registry — в том числе любой чисто-JSON запрос
MATCH_REGISTRY_INVALIDФайл реестра не читается: битый XLSX, CSV не в UTF-8
MATCH_REQUIRES_SCHEMAmatch без непустой output_schema — сверять нечего
MATCH_KEY_COLUMN_NOT_FOUNDkey_column не найдена в самом реестре
MATCH_FIELD_NOT_FOUNDКолонку не сопоставить с полем result; details.similar — подсказка, а не выбор за вас
MATCH_FIELD_AMBIGUOUSНесколько полей result подходят — укажите values_from
MATCH_NO_DATAresult пуст — сравнивать с реестром нечего
LANGUAGE_UNKNOWNlanguage вне ru/en/zh — допустимые значения в details.allowed
TRANSLATE_REQUIRES_SCHEMAtranslate без непустой output_schema — переводить нечего
FORMAT_UNKNOWNformat вне json/xlsx
XML_MALFORMEDФайл не является корректным XML (в том числе отклоненные XXE-конструкции)
XML_TOO_LARGEXML больше 10 МБ
XML_TOO_COMPLEXВ XML больше 200 000 элементов
FIELD_NOT_JSONForm-поле (output_schema/include/split/match) — невалидный JSON
FIELD_INVALIDПоле валидно синтаксически, но не той формы (объект вместо массива и т.п.)
BODY_NOT_JSON / BODY_NOT_OBJECTТело JSON-запроса не парсится или не является объектом

Обработка — тело {"success": false, "error": {…}}

HTTPКодКогда
413FILE_TOO_LARGEfile больше 50 МБ или registry больше 20 МБ (details.field укажет, что именно)
422ANONYMIZATION_INCOMPLETEPII-граница fail_closed: скраб не дал гарантии — вызов к внешней модели не выполнен. В details.residual только категории и счетчики, никогда сами значения
502LLM_RESULT_NOT_OBJECTМодель вернула текст вместо объекта по вашей схеме — испорченный ответ апстрима, не ошибка запроса
503PARSING_ERRORСбой разбора документа (Docling/OCR), детали — в details
503EXTRACTION_ERRORСбой на этапе извлечения
503PROVIDER_UNAVAILABLEПровайдер модели недоступен после исчерпания fallback-цепочки

Предохранители тарифа

Срабатывают до тяжелой обработки — за отклоненный запрос страницы не списываются. Механика и пороги — в «Аутентификации».

HTTPКодКогда
429RATE_LIMITEDПревышена скорость плана (страниц в минуту, скользящее окно 60 с). Заголовок Retry-After — реальные секунды до следующей допустимой отправки, интервал не надо угадывать
413PAYLOAD_TOO_LARGEОдин документ длиннее потолка страниц плана; details.max_pages_per_document. Оценка делается до парсинга
402QUOTA_EXCEEDEDМесячная квота страниц исчерпана; details.pages_used_this_month, details.pages_limit. Сбрасывается в начале календарного месяца
422KEY_LIMIT_REACHEDУ аккаунта уже пять активных ключей — отзовите ненужный в кабинете
403FREE_PLAN_NO_CHAT/v1/chat/completions недоступен на плане free (форма ошибки — detail.code, не error.code)

Остальное

HTTPКодКогда
401(строковый detail)Ключ отсутствует или неверен
401CABINET_HEADER_REQUIREDДоступ по сессии кабинета (кука session, без X-API-Key) без заголовка X-Requested-With: docai-cabinet — вторая линия защиты после SameSite=Lax
404TASK_NOT_FOUNDGET /v1/process/{task_id}: задача неизвестна, истекла (результат живет 1 час) или принадлежит другому ключу
503QUEUE_FULLОчередь async-обработки заполнена; тело — форма {"detail": {code…}}, в ответе есть заголовок Retry-After — повторите через указанное число секунд
PROCESSING_ERRORТолько в async: непредвиденный сбой фоновой обработки. В HTTP-ответе не встречается — приходит в теле поллинга как {"status": "error", "error": {…}}
INTERRUPTED_BY_RESTARTТолько в async: перезапуск сервиса застал задачу посреди обработки. Приходит в теле поллинга как {"status": "error", …} — отправьте документ повторно

Коды стабильны — это часть контракта; список аддитивный: новые коды могут появляться, существующие не переименовываются.

extraction_status

Приходит в metadata при 200 OK и отвечает на вопрос «что вообще получилось», независимо от HTTP-кода:

ЗначениеЗначит
fullДанные получены — моделью или детерминированно
partialЧасть секций или полей пуста (характерно для split)
fallbackДанных нет: модель их не вернула либо документ не удалось разобрать и модель не вызывалась вовсе
parse_onlyМодель не вызывалась, потому что не просили: нет output_schema

Это 200 OK в каждом случае. Проверяйте extraction_status так же внимательно, как HTTP-код: 200 означает «запрос обработан», а не «данные надежны».

Уверенности по полям в ответе нет

Ни общего confidence, ни уверенности по каждому полю в конверте нет: калибровка в единый эндпоинт не перенесена, metadata.input_quality_score всегда null. Сигналы качества здесь — extraction_status и warnings[]. Единственный confidence в ответе — в documents[], и он про другое: насколько сплиттер уверен в границе секции.

warnings[] — структурные предупреждения

Каждый warning — {code, field, severity, message}. field — путь до поля (supplier.inn, items[0].amount, documents[1]) или $document для документа целиком. severity: info (к сведению) → lowmedium (стоит перепроверить) → high (скорее всего неверно).

{
  "warnings": [
    {
      "code": "invalid_inn_length",
      "field": "supplier.inn",
      "severity": "high",
      "message": "ИНН «77» имеет некорректную длину…"
    }
  ]
}

Источников два, и они дополняют друг друга.

1. Проверки извлеченных данных

Работают всегда, независимо от вида задачи. Валидатор не только предупреждает, но и обнуляет заведомо неверное значение — например party2, дословно повторяющую party1.

КодSeverityКогда
invalid_inn_lengthhighИНН не 10 и не 12 цифр
invalid_kpp_lengthmediumКПП не 9 цифр
suspicious_amounthighСумма больше 100 млн ₽ без суммы прописью рядом
ocr_noisemediumВ тексте символы замены () — OCR не справился с частью листа
duplicate_partyhighИНН обеих сторон договора совпали — вторая сторона обнулена
missing_contractor_innmedium/lowУ стороны нет валидного ИНН
ambiguous_document_numberhighВ акте номер документа совпал с номером договора
totals_mismatchmediumИтог не сходится с суммой строк (с учетом НДС)
weight_invariant_violationhighНетто больше брутто — физически невозможно
sender_equals_carrierhighВ CMR отправитель и перевозчик — одно лицо
date_out_of_rangemediumДата вне [1990, 2100] — OCR мог спутать «24» с «1924»

2. Предупреждения самого эндпоинта

КодSeverityКогда
parse_emptyhighРазбор файла (или секции при split) дал пустой текст — модель не вызывается: выдумывать по пустому документу нечего. Со схемой это extraction_status: "fallback" и result: null
parse_thininfoТекст есть, но его подозрительно мало для такого числа листов — типичная картина нераспознанного скана. Обработка продолжается
llm_no_resulthighМодель вернула пустой результат (отказала вся fallback-цепочка)
section_unavailableinfoСекция fields/tables недоступна для этого входа (не-PDF)
tables_unavailableinfoРазбор таблиц упал — tables: null это сбой, а не «таблиц нет»
mode_ignoredinfomodebalanced: параметр принят, но на обработку не влияет
match_values_incompleteinfoВ поле сверки есть пропуски — coverage посчитан по остальным значениям
splitinfoПредупреждение сплиттера, проброшено как есть
split_truncatedinfoСекций больше, чем split.max_documents — хвост отброшен
split_result_truncatedhighНесогласованный результат сплиттера — часть секций отброшена
translation_failedmediumtranslate задан, но провайдер перевода недоступен — result отдан без перевода (translated: null)
translation_partiallowЧасть строк не переведена — оставлены как в оригинале, остальное translated заполнено
format_ignored_for_asyncinfoformat=xlsx вместе с async: true — файл забирается отдельно через GET /v1/process/{task_id}?format=xlsx
xlsx_export_failedmediumСборка Excel упала — результат отдан в JSON вместо файла (уже оплаченный результат не теряется)

Проверки «пусто/слишком мало текста» применяются только к файловому входу: короткий text вы прислали сами и осознанно.

Практика: как обрабатывать деградацию на своей стороне

  1. Проверяйте HTTP-статус и extraction_status/warnings[]. 200 OK означает «запрос обработан», а не «данным можно верить».
  2. На high-warning по конкретному полю не показывайте значение пользователю как окончательное без пометки. На fallback — рассмотрите повторный вызов с более узкой схемой или ручную проверку.
  3. parse_empty — сигнал о документе, а не о запросе: повтор с тем же файлом даст тот же результат. Нужен другой файл или лучшее качество скана.
  4. На 422 ANONYMIZATION_INCOMPLETE повтор скорее всего повторит ошибку — это осознанный отказ границы, а не временный сбой; нужен другой документ или self-hosted-контур (см. цены → «Бизнес»).
  5. На 502/503 (LLM_RESULT_NOT_OBJECT, PARSING_ERROR, EXTRACTION_ERROR, PROVIDER_UNAVAILABLE) — это временная деградация после исчерпания fallback-цепочки, повторите запрос с задержкой.
  6. На 503 QUEUE_FULL повторите через число секунд из заголовка Retry-After — это штатный ответ заполненной очереди, а не сбой. INTERRUPTED_BY_RESTART в теле поллинга — сигнал отправить документ заново: страницы за прерванную обработку не списываются повторно.

On this page