Перейти к содержанию

Ошибки

Формат ошибок Slicer API, HTTP-статусы и стабильные коды, какие ошибки можно повторять и как обрабатывать их в коде.

На этой странице

Каждая ошибка API приходит с HTTP-статусом и телом одного формата. Решения в коде принимайте по полю code: коды стабильны внутри v1 и не меняются, а текст message написан для человека — на языке аккаунта — и может уточняться.

Ответ с ошибкой
{
  "ok": false,
  "error": {
    "code": "insufficient_scope",
    "message": "Токену не хватает доступа: Запускать монтаж — тратит токены (montage:run)"
  }
}

Error

Поле Тип Описание
okобязательноеfalse
errorобязательноеobject
error.codeобязательноеstringСтабильный код ошибки в snake_case
error.messageобязательноеstringПонятный текст на языке аккаунта
error.fieldsRecord<string, string>Ошибки по полям
error.retryAfterSecintegerЧерез сколько секунд повторить
  • fields — ошибки по полям тела запроса, если запрос не прошёл проверку: { "uploadIds": "…" }.
  • retryAfterSec — через сколько секунд повторить (при превышении лимита запросов).

Статусы

Статус Когда Что делать
400тело или параметры не прошли проверку, файл не подходитисправить запрос; подробности — в fields и message
401нет токена, токен неверный, отозван или истёкпроверить токен; при token_expired и token_revoked — выпустить новый
402не хватает токенов на балансе или исчерпан лимит расходов токенапополнить баланс в приложении или поднять лимит токена
403у токена нет нужной области доступавыпустить токен с нужными областями
404ресурс не найден или принадлежит другому аккаунтупроверить id; чужие ресурсы для API не существуют
409конфликт состояния: цена изменилась, документ устарел, проект занятперечитать состояние и повторить
410ссылка на загрузку истекла или уже использованасоздать новую загрузку
429превышен лимит запросовподождать Retry-After секунд
5xxвременный сбой сервера или ИИповторить позже с тем же Idempotency-Key

Частые коды

Код Статус Значение
unauthorized401нет заголовка Authorization: Bearer
invalid_token401токен не найден или содержит ошибку
token_revoked401токен отозван, приложение отключено или аккаунт удалён
token_expired401срок токена истёк
insufficient_scope403у токена нет нужной области; список недостающих — в message
not_found404проект, загрузка или вебхук не найдены
insufficient_tokens402на балансе меньше, чем стоит операция
spend_limit_exceeded402операция не помещается в месячный лимит расходов токена
cost_changed409confirmCost не совпал с текущей ценой — запросите цену заново
stale_document409ревизия документа устарела — перечитайте rev и повторите правку
busy409над проектом уже идёт работа (монтаж, рендер) — дождитесь окончания
not_ready409ролик ещё не готов для этой операции
too_many_drafts409слишком много черновиков проектов без ИИ-режиссёра — экспортируйте или удалите лишние
daily_export_limit429бесплатные экспорты проектов без ИИ-режиссёра на сегодня закончились — повторите через retryAfterSec
manual_disabled503проекты без ИИ-режиссёра временно недоступны
request_in_progress409запрос с этим Idempotency-Key ещё выполняется
bad_idempotency_key409Idempotency-Key не в формате: 8–128 символов A–Z a–z 0–9 _ -
bad_cursor400курсор страницы повреждён — начните с первой страницы
platform_requires_app400публикация в эту соцсеть возможна только из приложения Slicer
rate_limit_exceeded429слишком много запросов в минуту на токен
ai_unavailable503ИИ временно недоступен — повторите позже, токены не списаны
api_disabled503публичное API временно выключено
internal_server_error500непредвиденная ошибка — повторите позже

Ошибки загрузки файлов (unsupported_format, file_too_large, resolution_too_low, link_expired и другие) описаны в разделе Загрузка файлов.

Все коды

Код HTTP Когда
unauthorized401Нет токена или он неверный
token_revoked401Токен отозван или приложение отключено от аккаунта
token_expired401Срок токена истёк
insufficient_scope403У токена нет нужной области доступа (см. WWW-Authenticate)
forbidden403Действие недоступно этому аккаунту
not_found404Объекта нет или он принадлежит другому аккаунту
bad_request400Неверный запрос; ошибки по полям — в fields
conflict409Объект в состоянии, которое не позволяет действие
stale_document409Документ изменился: получите свежую rev и повторите
busy409Проект занят идущим запуском
not_ready409Ролик или загрузка ещё не готовы
request_in_progress409Запрос с этим Idempotency-Key ещё выполняется
cost_changed409Цена изменилась: подтвердите новую cost
insufficient_tokens402На балансе не хватает токенов
spend_limit_exceeded402Достигнут месячный лимит расходов токена
rate_limit_exceeded429Слишком много запросов: подождите Retry-After секунд
file_too_large400Файл больше допустимого для своего вида
unsupported_format400Формат файла не поддерживается
empty_file400Файл пустой
parts_missing400До complete загружены не все части
upload_not_active409Загрузка уже завершена или отменена
link_expired410Срок одноразовой ссылки загрузки истёк
link_used410Одноразовая ссылка загрузки уже использована
too_many_imports429Слишком много одновременных загрузок по адресу
platform_requires_app400Публикация в эту платформу — только из приложения Slicer
ai_unavailable503ИИ-сервис временно недоступен: повторите позже

Что можно повторять

Повторять с тем же Idempotency-Key безопасно: если первый запрос дошёл до сервера, вы получите его ответ, а не вторую операцию (Идемпотентность).

  • Сеть оборвалась, 500, 502, 503, 504 — повтор с экспоненциальной паузой: 1, 2, 4, 8 секунд и так далее, не больше 5–6 попыток.
  • 429 — подождать столько секунд, сколько указано в Retry-After (или retryAfterSec), затем повторить.
  • 409 request_in_progress — первый запрос ещё выполняется; подождите пару секунд и повторите с тем же ключом.
  • 409 cost_changed, stale_document — не повторять вслепую: перечитать цену или документ, при необходимости показать пользователю и отправить запрос заново.
  • 400, 401, 402, 403, 404 — повтор не поможет, нужно исправить запрос, токен или баланс.
JavaScript
Python
slicer.mjs
import { randomUUID } from 'node:crypto';

export class SlicerError extends Error {
  constructor(status, error) {
    super(`${status} ${error.code}: ${error.message}`);
    this.status = status;
    this.code = error.code;
    this.fields = error.fields;
    this.retryAfterSec = error.retryAfterSec;
  }
}

const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

/** Запрос к API с повторами: один Idempotency-Key на все попытки. */
export async function slicer(method, path, body, { attempts = 5 } = {}) {
  const key = method === 'POST' ? randomUUID() : undefined;
  for (let attempt = 1; ; attempt += 1) {
    let response;
    try {
      response = await fetch(`https://api.slicer.media/v1${path}`, {
        method,
        headers: {
          Authorization: `Bearer ${process.env.SLICER_TOKEN}`,
          ...(body ? { 'Content-Type': 'application/json' } : {}),
          ...(key ? { 'Idempotency-Key': key } : {}),
        },
        body: body ? JSON.stringify(body) : undefined,
      });
    } catch (networkError) {
      if (attempt >= attempts) throw networkError;
      await sleep(2 ** attempt * 500);
      continue;
    }
    const json = await response.json();
    if (json.ok) return json.data;
    const retry = RETRYABLE.has(response.status) || json.error.code === 'request_in_progress';
    if (!retry || attempt >= attempts) throw new SlicerError(response.status, json.error);
    const wait = Number(response.headers.get('Retry-After')) || 2 ** attempt * 0.5;
    await sleep(wait * 1000);
  }
}

Что дальше