Kody błędów KSeF API 2.0: 440, 450, 21405 i reszta — co znaczą i jak je naprawić w enova365, Optimie i Subiekcie
Po co ten tekst
Po siedmiu miesiącach obowiązkowego KSeF zmieniły się pytania, które dostaję od biur rachunkowych i integratorów. Nikt już nie pyta, czy ERP umie wysyłać. Wklejają numer. „Mamy 440". „Optima pisze odrzucono, co to jest 450?". „Integrator mówi 21405 — to po naszej stronie czy po jego?".
Odpowiedzi są w specyfikacji API samego Ministerstwa Finansów: w pliku open-api.json i dokumentacji w oficjalnym repozytorium CIRFMF/ksef-docs na GitHubie. Tyle że są rozrzucone po opisach schematów, a rzecz, na której potyka się prawie każdy, nie jest nigdzie napisana wprost: KSeF zgłasza błędy na trzech osobnych poziomach i ten sam numer znaczy na każdym z nich coś innego.
Zebrałem to w jednym miejscu. Wszystko w tabelach pochodzi ze specyfikacji w stanie na listopad 2026 r. Tam, gdzie opisuję, co pokazują enova365, Optima czy Subiekt, opieram się na własnej pracy z tymi systemami, a wersje się różnią, więc sprawdźcie swoją.
Trzy poziomy, jedna pula numerów
Kiedy coś pójdzie nie tak, KSeF mówi o tym na jednym z trzech poziomów:
- Wyjątek w odpowiedzi HTTP. Odrzucone zostało samo żądanie. W treści jest
exception.exceptionDetailList[]z pięciocyfrowymexceptionCode(21xxx, 25xxx). Nic nie trafiło do kolejki. Nie ma statusu faktury, bo w systemie nie ma faktury. - Status sesji. Otworzyliście sesję interaktywną albo wsadową i padła sesja jako całość, np. minął czas, a nic w niej nie było.
- Status faktury. Fakturę przyjęto do przetwarzania, a potem odrzucono. Obiekt statusu ma trzycyfrowy
code,description,details[], a przy niektórych kodach takżeextensions.
Trzycyfrowe kody się pokrywają. 440 przy fakturze to duplikat. 440 przy sesji to sesja anulowana. 440 przy operacji na uprawnieniach to „operacja niedozwolona dla wskazanych powiązań identyfikatorów". 450 przy fakturze to błąd semantyki, a 450 przy uwierzytelnianiu to zły token. Jeśli w logu stoi „błąd KSeF 440" i nie wiadomo, z jakiego obiektu pochodzi, taki wpis jest bezużyteczny. W większości integracji pisanych „na szybko" właśnie tak to wygląda.
Kody statusu faktury
To te kody naprawdę widzą księgowe, bo ERP-y zamieniają je na „odrzucono".
| Kod | Co mówi specyfikacja | Co to zwykle znaczy | Co zrobić |
|---|---|---|---|
| 100 / 150 | Przyjęta / trwa przetwarzanie | To nie błąd, faktura czeka w kolejce | Odczekać, sprawdzić ponownie |
| 200 | Sukces | Nadany numer KSeF | Zapisać numer, pobrać UPO |
| 405 | Anulowano z powodu błędu sesji | Padła sesja, nie ta faktura | Naprawić sesję, wysłać ponownie |
| 410 | Nieprawidłowy zakres uprawnień | Poświadczenie nie pozwala wystawiać za ten NIP | Nadać właściwe uprawnienie w KSeF |
| 415 | Brak możliwości wysłania faktury z załącznikiem | Załączniki niezgłoszone albo wysyłka w sesji interaktywnej | Zgłosić w e-Urzędzie Skarbowym, wysłać wsadowo |
| 430 | Błąd weryfikacji pliku faktury | Niepoprawny XML, BOM, złe kodowanie, niezgodność ze schemą | Poprawić generator |
| 435 | Błąd odszyfrowania pliku | Źle wykonane szyfrowanie AES/RSA | Błąd integracji, nie księgowej |
| 440 | Duplikat faktury | Ten sam NIP + RodzajFaktury + P_2 już jest w KSeF | Najpierw odczytać originalKsefNumber |
| 450 | Błąd weryfikacji semantyki | Daty, sumy kontrolne NIP, reguły biznesowe | Poprawić dane, wysłać ponownie |
| 500 | Nieznany błąd | Po stronie MF albo coś niesklasyfikowanego | Ponowić później, potem zgłosić z numerem referencyjnym |
| 550 | Operacja anulowana przez system | Wewnętrzne przerwanie przetwarzania | Ponowić |
Kilka z nich wymaga więcej niż jednej komórki tabeli.
430 to prawie zawsze generator. Dokumentacja weryfikacji jest bezlitosna: UTF-8 bez BOM (wystarczą trzy bajty 0xEF 0xBB 0xBF na początku pliku), żadnych instrukcji przetwarzania, żadnego prologu z innym kodowaniem i lista niezalecanych zakresów Unicode, które pojawiają się, kiedy ktoś wklei nazwę towaru z PDF-a. Tu siedzą też limity rozmiaru: 1 MB bez załączników, 3 MB z załącznikami.
450 naprawia się w danych. Większość przypadków, które widziałem, bierze się z dwóch reguł. Data wystawienia P_1 nie może być późniejsza niż moment przyjęcia dokumentu przez KSeF, więc faktura z jutrzejszą datą nie przejdzie. A sumy kontrolne NIP sprawdzane są dla Podmiot1, Podmiot2, Podmiot3 i PodmiotUpowazniony, ale tylko na produkcji. Ten drobiazg oznacza, że plik może przejść wszystkie testy na środowisku testowym i paść pierwszego dnia na produkcji. Jeśli w danych testowych macie zmyślone NIP-y, tej reguły nie przetestowaliście.
415 zaskakuje, bo poprawka nie jest techniczna. Wysyłkę faktur z załącznikami trzeba wcześniej zgłosić w e-Urzędzie Skarbowym, a działa ona tylko w trybie wsadowym. Jedyny wyjątek to korekta techniczna faktury offline.
Pułapka 440
To przez ten błąd powstał cały ten tekst.
KSeF wykrywa duplikaty globalnie, po kombinacji NIP-u sprzedawcy (Podmiot1:NIP), rodzaju faktury (RodzajFaktury) i numeru faktury (P_2), i trzyma tę unikalność przez 10 pełnych lat od końca roku, w którym fakturę wystawiono. Czyli 440 znaczy: faktura o tym numerze od tego sprzedawcy już jest w KSeF.
Odruchowa naprawa to nowy numer i ponowna wysyłka. Zwykle to błąd.
Najczęściej 440 powstaje tak: pierwsza wysyłka się udała, KSeF nadał numer, a ERP nigdy nie zapisał odpowiedzi. Przekroczony czas, restart usługi, sprawdzenie statusu, które się nie wykonało. Dokument leży w księgach bez numeru KSeF, ktoś klika „wyślij" jeszcze raz, a KSeF zgodnie z prawdą odpowiada, że już go ma. Jeśli w tym momencie zmienicie numer i wyślecie ponownie, wystawicie drugą fakturę na tę samą sprzedaż. W systemie krajowym są wtedy dwie prawnie istniejące faktury, nabywca widzi obie, a jedną trzeba będzie wycofać korektą.
Specyfikacja daje wyjście. Status faktury z kodem 440 ma extensions z polami originalKsefNumber i originalSessionReferenceNumber. Przeczytajcie je. Jeśli oryginał to ta sama faktura, przypiszcie ten numer KSeF do dokumentu i pobierzcie UPO. Koniec, bez nowej faktury.
Druga przyczyna to prawdziwa kolizja: dwie jednostki wystawiające za ten sam NIP z kolidującymi seriami. Oddziały, jednostki organizacyjne JST, sklep internetowy i ERP numerujące równolegle. Specyfikacja mówi wprost, że takie jednostki muszą uzgodnić zasady numeracji. Wtedy faktura naprawdę potrzebuje nowego numeru, a serie trzeba poprawić, żeby za tydzień nie było powtórki.
Tak wygląda ta decyzja w kodzie, w takiej postaci, w jakiej zapisuję ją w zadaniu uzgadniającym:
def handle_invoice_status(doc, status):
code = status["code"]
if code == 200:
return mark_sent(doc, ksef_number=status_ksef_number(status))
if code in (100, 150):
return poll_later(doc)
if code == 440:
ext = status.get("extensions") or {}
original = ext.get("originalKsefNumber")
if original and same_invoice(doc, original): # porównaj skrót, kwoty, nabywcę
return mark_sent(doc, ksef_number=original) # odzyskaj, nie zmieniaj numeru
return escalate(doc, "kolizja numeru: popraw serię, potem nowy numer")
if code in (500, 550):
return retry_later(doc)
return escalate(doc, f"{code}: {status['description']} {status.get('details')}")
Najwięcej uwagi wymaga same_invoice: pobierzcie oryginał po numerze KSeF i porównajcie nabywcę, sumy, a najlepiej skrót XML-a, który macie u siebie. Porównanie samego numeru to dokładnie ten błąd, którego chcecie uniknąć.
Kody sesji i wyjątki 21xxx
Statusy sesji obchodzą głównie tego, kto utrzymuje integrację. W sesji wsadowej 405 to błąd weryfikacji elementów paczki, 415 i 435 to błędy odszyfrowania, 420 oznacza przekroczenie limitu 10 000 faktur w sesji, 430 to błąd dekompresji, 440 to sesja anulowana (przekroczony czas wysyłki albo nie przesłano faktur), a 445 znaczy, że weryfikacja nie znalazła ani jednej poprawnej faktury. Sesja interaktywna ma te same 415, 440 i 445.
Pięciocyfrowe wyjątki przychodzą w odpowiedzi HTTP i znaczą, że żądanie w ogóle nie weszło:
| Wyjątek | Znaczenie |
|---|---|
| 21401 | Dokument niezgodny ze schemą XSD |
| 21402 / 21403 | Rozmiar albo skrót pliku nie zgadza się z zadeklarowanym |
| 21405 | Błąd walidacji danych wejściowych (w szczegółach komunikat walidatora) |
| 21155 | Osiągnięty limit faktur w sesji |
| 21180 | Status sesji nie pozwala na operację (np. wysyłka do zamkniętej sesji) |
| 21208 | Przekroczony czas oczekiwania na upload/finish, sesja anulowana |
| 21164 / 21165 | Faktura o tym numerze KSeF nie istnieje / nie jest jeszcze dostępna |
| 21184 | Sesja tymczasowo niedostępna, prace serwisowe |
Przy 21165 jedna uwaga dla każdego, kto buduje import faktur zakupowych: faktura może być już przetworzona, a przez jakiś czas jeszcze niedostępna do pobrania. Traktujcie to jako „spróbuj później", a nie „nie istnieje".
429 to nie awaria
KSeF ogranicza ruch na każdym chronionym endpoincie. Żądania liczone są dla pary kontekst i adres IP: NIP, w którego imieniu się uwierzytelniliście, plus adres, z którego się łączycie. Okna przesuwają się co sekundę, minutę i godzinę i wszystkie trzy obowiązują jednocześnie. Jeśli przekroczycie którekolwiek, dostaniecie HTTP 429 z nagłówkiem Retry-After w sekundach. Długość blokady jest dynamiczna i rośnie przy powtarzających się przekroczeniach.
Dla biura rachunkowego wynikają z tego dwie rzeczy. Biuro, które pobiera faktury klienta ze swojego IP, ma osobny budżet od klienta pobierającego ze swojego, więc nie zjadają sobie nawzajem limitu. Ale rotowanie wielu adresów IP na jednym kontekście, żeby obejść limit, dokumentacja wprost wymienia jako coś, co może zostać uznane za zagrożenie bezpieczeństwa. Specyfikacja wspomina też o wyższych limitach pobierania między 20:00 a 6:00, czyli tam, gdzie i tak powinny działać nocne importy.
Co pokazują enova365, Optima i Subiekt
Żaden z tych trzech systemów nie pokazuje pełnego obrazu na liście faktur. W praktyce to jest właśnie ten problem.
Udokumentowany filtr KSeF w Optimie ma statusy takie jak „Nie wysłano", „Oczekuje na odbiór UPO", „Odebrano UPO", „Awaria" i „Odrzucono przez KSeF". enova365 i Subiekt też sprowadzają wynik do stanu na poziomie dokumentu. Przy 200 to wystarcza. Przy każdym innym kodzie „odrzucono" ukrywa, czy patrzycie na 430 (dzwonić do dostawcy albo integratora), 450 (poprawić dane), 410 (poprawić uprawnienia) czy 440 (prawdopodobnie już wysłana, nie ruszać numeru). Kod i szczegóły są we wszystkich trzech w logu komunikacji z KSeF albo w historii KSeF dokumentu, a nie w kolumnie listy.
Dlatego moja stała rada dla biur: kiedy klient mówi „KSeF odrzucił", najpierw poproście o trzycyfrowy kod i linijkę szczegółów. Klient znajdzie to w minutę, a wy oszczędzicie godzinę zgadywania. A w integracjach, które buduję, czy to zadanie na WebAPI enova365, usługa na Sferze Subiekta GT, czy cokolwiek na Optimie, każde odrzucenie jest logowane z poziomem, kodem, szczegółami i rozszerzeniami, w jednym miejscu, które ktoś faktycznie czyta.
To ta sama lekcja co przy trzech cichych stanach KSeF z września: odrzucenie, którego nikt nie czyta, to po prostu niewidoczna faktura z dodatkowymi krokami. Nocne zadanie tylko do odczytu, które wypisuje każdy dokument ze statusem innym niż 200, jego kod i wiek w dniach, to najtańsza kontrola KSeF, jaka istnieje. Przy 440 może od razu zaproponować odzyskany numer KSeF do zatwierdzenia przez człowieka. Tak właśnie zaczynają się agenci AI, których wdrażam w firmach: od odczytu i propozycji, nie od zapisu.
Jeśli widzicie odrzucenia KSeF, których nie umiecie sklasyfikować, albo 440, które ktoś „naprawiał" nowymi numerami, napiszcie do mnie. Zadanie uzgadniające, które je porządkuje, stawiam na enova365, Optimie lub Subiekcie w dzień lub dwa i od pierwszej godziny działa tylko w trybie odczytu.
Sprawdź kontrahenta w kilka sekund — za darmo
KsięgoAI sprawdza NIP na białej liście i w VIES, ocenia ryzyko i zapisuje dowód należytej staranności. Działa na Telegramie i w Claude.
Wypróbuj KsięgoAI→Porozmawiajmy o Twoim projekcie
Bezpłatna 30-minutowa konsultacja. Sprawdzimy, czy i jak mogę pomóc.



