Błędy
Każdy kod biznesowy, co oznacza i które błędy warto ponawiać.
Gdy wywołanie się nie uda, odpowiedź zawiera liczbę (code), która mówi, jaki to problem, oraz zdanie (msg) dla człowieka. Rozgałęziaj logikę według liczby; zdanie pokazuj albo zapisuj w logach.
Koperta
{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }codeto kod biznesowy.200oznacza sukces; pozostałe są wypisane niżej.- Przy błędzie nie ma pola
data— wyjątkiem są niektóre błędy biblioteki zasobów, które dodajądata.reason. - Status HTTP też jest ustawiany poprawnie (
40201przychodzi z HTTP402). Opieraj się na jednym z nich, bez mieszania — polecamy dokładniejszycode. request_idjest też w nagłówkuX-Request-Id. Podaj go, pisząc do pomocy.
Jeśli odpowiedź w ogóle nie jest JSON-em — np. stroną HTML lub tekstową z sieci pośredniczącej — obsłuż ją według statusu HTTP i zapisz w logach początek treści.
Język komunikatów o błędach
msg i errorMessage nieudanego zadania są domyślnie po angielsku. Wyślij Accept-Language z wartością en, ja, ko, de, fr, es, pt-BR, ru, it lub pl albo ustaw język błędów API w konsoli, a dostaniesz je w tym języku; pozostałe języki wracają do angielskiego. Odpowiedzi z błędem mają nagłówek Content-Language. Kody nigdy nie zmieniają się z językiem — kolejny powód, by nigdy nie parsować msg.
Gdy model poda konkretny powód niepowodzenia (np. nieczytelny obraz referencyjny), errorMessage przekazuje go po angielsku, bez szczegółów wewnętrznych.
Kody ogólne
code | HTTP | Znaczenie | Ponawiać? |
|---|---|---|---|
200 | 200 / 202 | Sukces | — |
400 | 400 | Zapytanie lub jego input są niepoprawne; msg wskazuje problem | Nie — popraw |
40003 | 400 | Przesłany plik nie zgadza się z biletem (rozmiar, typ lub zawartość) | Nie — nowy bilet, nowe przesłanie |
40004 | 400 | Zapytanie jest poprawne, ale model nie obsłuży teraz tej kombinacji ustawień | Nie — zmień ustawienie wskazane w msg |
401 | 401 | Brak klucza, nieznany lub cofnięty (szczegóły) | Nie |
403 | 403 | Brak uprawnień (zobacz Uprawnienia) | Nie |
404 | 404 | Nie istnieje albo należy do innego klucza | Nie |
409 | 409 | Konflikt z bieżącym stanem (kiedy ponawiać) | Czasem |
40901 | 409 | Wycena wygasła albo cena się zmieniła | Wyceń ponownie |
413 | 413 | Treść zapytania jest za duża | Nie — prześlij media zamiast je osadzać |
429 | 429 | Za dużo zapytań | Tak — po Retry-After |
500 | 500 | Coś zepsuło się po naszej stronie | Tak — z rosnącą przerwą |
503 | 503 | Usługa pomocnicza jest chwilowo niedostępna | Tak — po Retry-After |
Saldo i limity
code | HTTP | Znaczenie | Co zrobić |
|---|---|---|---|
40201 | 402 | Za mało kredytów na blokadę tego zadania | Kup kredyty i wyślij ponownie |
40202 | 402 | Osiągnięto limit wydatków klucza | Podnieś go w konsoli API albo poczekaj na wyzerowanie |
Uprawnienia
code | HTTP | Znaczenie | Co zrobić |
|---|---|---|---|
40301 | 403 | Ten klucz nie może wywoływać tego modelu | Dodaj model do listy dozwolonych dla klucza |
40302 | 403 | Zapytanie przyszło z adresu spoza listy dozwolonych IP | Dodaj adres albo usuń ograniczenie |
40303 | 403 | Usługa nie jest dostępna w tym regionie | Nie da się tego obejść |
40310 | 403 | Adres e-mail konta nie jest jeszcze potwierdzony | Otwórz link z maila rejestracyjnego; klucz zadziała od razu po potwierdzeniu |
Zwykłe 403 przy ważnym kluczu oznacza, że konto nie może teraz korzystać z API; napisz do pomocy i podaj request_id.
Dostępność modeli
code | HTTP | Znaczenie | Ponawiać? |
|---|---|---|---|
50301 | 503 | Model nie przyjmuje teraz zadań | Tak, później — albo wybierz inny model |
40004 | 400 | Model działa, ale nie z tymi ustawieniami | Nie — zmień ustawienie |
Żaden z nich nie tworzy zadania ani nie blokuje kredytów.
Kody niepowodzeń zadań
Zadanie przyjęte, które potem się nie powiodło, ma state: "failed" i jedną z tych wartości w errorCode. Lista jest zamknięta: każdą inną wartość traktuj jak upstream_failed.
errorCode | Znaczenie | Ponawiać? |
|---|---|---|
invalid_request | Model nie przyjął parametrów | Nie — popraw je |
unsupported_combination | Ta kombinacja ustawień nie jest teraz obsługiwana | Nie — zmień ustawienie |
content_rejected | Model odrzucił tę treść | Nie — zmień prompt albo materiał referencyjny |
invalid_asset | Pliku referencyjnego nie da się odczytać albo nie jest już dostępny | Prześlij go ponownie i utwórz nowe zadanie |
rate_limited | Usługa generowania była przeciążona | Tak, z rosnącą przerwą |
upstream_unavailable | Generowanie jest chwilowo niedostępne | Tak, za chwilę |
generation_failed | Generowanie ruszyło, ale nic nie zwróciło | Może — te same dane wejściowe często zawodzą tak samo |
timeout | Zadaniu skończył się czas | Może — spróbuj krótszego wyniku lub niższej rozdzielczości |
upstream_failed | Wszystko inne | Tak, z rosnącą przerwą |
Nieudane zadanie zwalnia blokadę, więc nic nie kosztuje (nieliczne modele pobierają opłatę za odmowę z powodu treści i piszą o tym na swojej stronie). errorMessage opisuje powód słowami; rozgałęziaj logikę tylko według errorCode.
Kiedy warto ponowić 409
| Sytuacja | Ponawiać? |
|---|---|
Dwa zapytania z tym samym Idempotency-Key przyszły jednocześnie | Tak, tym samym kluczem, po krótkiej przerwie |
Idempotency-Key został użyty do innego zapytania albo przez inny klucz | Nie |
| Przesłany plik lub wynik jest jeszcze przetwarzany | Tak, po kilku sekundach |
jobs/retry dla zadania, które jeszcze się nie zakończyło | Tak, gdy będzie w stanie końcowym |
jobs/retry dla zadania zakończonego sukcesem | Nie — utwórz nowe zadanie |
| Bilet przesyłania albo przesłany plik wygasł | Nie — prześlij ponownie |
Jak napisać ponowienie
Ponawiaj tylko to, co może się udać za drugim razem, czekaj na Retry-After, jeśli przyszedł, a w przeciwnym razie zwiększaj przerwę wykładniczo:
const RETRYABLE = new Set([429, 500, 503, 50301]);
async function call(path, init, attempts = 4) {
for (let i = 0; i < attempts; i++) {
const res = await fetch(`https://api.popnow.ai/api/v1${path}`, init);
if (!res.headers.get('content-type')?.includes('json')) {
throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
}
const body = await res.json();
if (body.code === 200) return body.data;
if (!RETRYABLE.has(body.code)) throw new Error(`${body.code} ${body.msg} (${body.request_id})`);
const wait = Number(res.headers.get('retry-after')) * 1000 || 2 ** i * 1000 + Math.random() * 500;
await new Promise((r) => setTimeout(r, wait));
}
throw new Error('out of retries');
}import random, time, requests
RETRYABLE = {429, 500, 503, 50301}
def call(method, path, attempts=4, **kwargs):
for i in range(attempts):
res = requests.request(method, f"https://api.popnow.ai/api/v1{path}", timeout=60, **kwargs)
try:
body = res.json()
except ValueError:
raise RuntimeError(f"HTTP {res.status_code}: {res.text[:200]}")
if body["code"] == 200:
return body["data"]
if body["code"] not in RETRYABLE:
raise RuntimeError(f'{body["code"]} {body["msg"]} ({body.get("request_id")})')
retry_after = res.headers.get("retry-after")
time.sleep(float(retry_after) if retry_after else 2**i + random.random() / 2)
raise RuntimeError("out of retries")Ponawiając createTask, zawsze wysyłaj ten sam Idempotency-Key, żeby zapytanie, które w rzeczywistości się udało, nie zostało wykonane i rozliczone dwa razy.
Diagnozowanie
- Zapisz
request_idicode. - Znajdź zadanie w logach konsoli API po ID zadania lub
request_id. - Nadal utknąłeś? Napisz do pomocy, podając
request_id, godzinę (UTC) icode. Nigdy nie wysyłaj swojego klucza API.

