GitHub

Error Reference

SDK exception hierarchy, attributes, KSeF error codes, hints, and polling timeout classes.

On this page

ksef2 raises SDK exceptions for failures it can classify. Transport failures raised before KSeF returns a parsed API response remain httpx.HTTPError exceptions. Presigned external-storage transfers use the dedicated exceptions described below instead of KSeF API/authentication classification.

Branch on the class or on ksef_code

Handle errors by exception class, or by ksef_code for KSeF API errors. Never branch on the message text: messages are written for people, carry the current KSeF wording and can change between releases. The same goes for hint: show it, log it, but do not parse it.

Base classes

ClassBasecodeMain attributes
KSeFExceptionExceptionSDK_ERRORcontext, hint
KSeFApiErrorKSeFExceptionAPI_ERRORstatus_code, ksef_code, exception_code, trace_id, details, response
KSeFNotReadyErrorKSeFApiErrorNOT_READYAPI error attributes
KSeFAuthErrorKSeFApiErrorAUTH_ERRORAPI error attributes
KSeFAuthenticationExpiredErrorKSeFAuthErrorAUTHENTICATION_EXPIREDAPI error attributes
KSeFRateLimitErrorKSeFApiErrorRATE_LIMIT_ERRORretry_after and the API error attributes
KSeFExternalTransferErrorKSeFExceptionEXTERNAL_TRANSFER_ERRORoperation, host, reference_number, part_ordinal, status_code, outcome_ambiguous
KSeFBatchUploadErrorKSeFExternalTransferErrorBATCH_UPLOAD_ERRORExternal-transfer attributes plus recovery_state()
KSeFInvoiceRejectedErrorKSeFSessionErrorINVOICE_REJECTEDinvoice_reference_number, invoice_status_code, description, details, extensions, status

Catch narrower subclasses before KSeFException when the workflow has a specific recovery action.

try:
    result = auth.invoices.query_metadata(filters=filters)
except KSeFRateLimitError as exc:
    retry_after = exc.retry_after
except KSeFNotReadyError:
    ...  # KSeF has not finished yet; wait and ask again
except KSeFApiError as exc:
    if exc.ksef_code == 21405:
        details = exc.details
    trace_id = exc.trace_id  # quote it when contacting KSeF support
except KSeFException as exc:
    context = exc.context
except httpx.HTTPError as exc:
    transport_error = exc
python

SDK exception classes

ClasscodeRaised for
KSeFClientClosedErrorCLIENT_CLOSEDRoot client or session client used after close.
KSeFUnsupportedEnvironmentErrorUNSUPPORTED_ENVIRONMENTTEST-only branch or flow used outside Environment.TEST.
KSeFValidationErrorVALIDATION_ERRORInvalid SDK input, invalid response payload, invalid profile config, or invalid session/batch arguments.
KSeFArgumentErrorARGUMENT_ERRORA call combined arguments the SDK does not allow, such as both or neither of form_code and state. Subclass of both KSeFValidationError and TypeError.
KSeFInvoiceRenderingErrorINVOICE_RENDERING_ERROROptional XSLT/PDF rendering failures.
KSeFEncryptionErrorENCRYPTION_ERRORToken, symmetric-key, invoice encryption, or decryption failure.
KSeFSessionErrorSESSION_ERRORSession-state violation, such as using a closed session. Base class of KSeFInvoiceRejectedError.
KSeFInvoiceRejectedErrorINVOICE_REJECTEDKSeF finished processing an online-session invoice and rejected it (InvoiceSubmission.wait()).
KSeFExportFailedErrorEXPORT_FAILEDKSeF finished an invoice export without a package: it failed, was cancelled by the system or expired (ExportJob.wait()).
KSeFPermissionOperationFailedErrorPERMISSION_OPERATION_FAILEDKSeF finished a permission grant or revoke without applying it (PermissionOperation.wait()).
KSeFCertificateEnrollmentFailedErrorCERTIFICATE_ENROLLMENT_FAILEDKSeF rejected, cancelled or failed a certificate enrollment (CertificateEnrollment.wait()).
KSeFAuthTokenRedemptionErrorAUTH_TOKEN_REDEMPTION_ERRORA one-shot authentication redemption lost its response and may have succeeded.
KSeFExternalTransferErrorEXTERNAL_TRANSFER_ERRORA presigned external-storage upload or download was rejected or lost its response.
KSeFBatchUploadErrorBATCH_UPLOAD_ERRORA batch-part upload failed while protected recovery state remains available.
NoCertificateAvailableErrorNO_CERTIFICATE_AVAILABLENo valid certificate exists for signing or encryption usage.
KSeFMetadataPaginationErrorMETADATA_PAGINATION_ERRORMetadata pagination cannot continue safely.

KSeFAuthTokenRedemptionError.outcome_ambiguous is always True. Do not retry the redemption: KSeF may already have consumed the temporary authentication token even though the response did not reach the caller.

External transfer attributes and batch recovery

Presigned storage responses are not KSeF API responses. A storage 403, for example, raises KSeFExternalTransferError, not KSeFAuthError. Its message and context contain only the sanitized host, operation, workflow reference, part ordinal, status code, and ambiguity flag; the signed URL is not included.

AttributeTypeMeaning
operationLiteral["upload", "download"]External transfer that failed.
hoststrStorage hostname without path, query, or signature.
reference_numberstrExport or batch workflow reference.
part_ordinalintOne-based package part ordinal.
status_code`intNone`
outcome_ambiguousboolTrue when an upload may have reached storage despite a lost response.

KSeFBatchUploadError.recovery_state() deliberately reveals the sensitive BatchSessionResumeState required for recovery. It is excluded from the error message and context because it contains encryption material and presigned URLs. Store it only in protected credential storage, and do not blindly retry an upload when outcome_ambiguous is True.

The original httpx failure remains available through __cause__. Avoid logging the cause indiscriminately because raw HTTP diagnostics can include the signed URL.

API error attributes

KSeFApiError is raised for parsed KSeF 4xx and 5xx responses. Specialized subclasses are used for authentication/authorization failures, rate limits and resources KSeF has not made available yet.

AttributeTypeMeaning
status_codeintHTTP status returned by KSeF.
ksef_code`intNone`
exception_codeExceptionCodeThe same code as an enum when the SDK lists it; UNKNOWN_ERROR otherwise. Kept for compatibility.
trace_id`strNone`
detailslist[str]Detail messages KSeF attached to the error; empty when none.
hint`strNone`
response`BaseModelNone`

The same values are available in context: status_code, ksef_code, trace_id, details and hint (when there is one).

Use response.model_dump() or response.model_dump_json() for structured diagnostics when response is not None.

Message format

Every KSeF API error is formatted the same way, whatever shape the response had (application/problem+json, the older exception payload, another JSON body, text or nothing). The SDK picks the parser from the response Content-Type: application/problem+json is read as Problem Details, application/json as the older payload (except a 401, which KSeF sends as Problem Details labelled application/json), and anything else becomes a short snippet:

KSeF rejected <METHOD> <path> (HTTP <status>, KSeF code <code>): <description>
Details: <detail>; <detail>
Trace ID: <trace id>
Hint: <hint>
text

The KSeF code part is left out when the response has no code, and the Details, Trace ID and Hint lines appear only when there is something to show. By default the SDK sends X-Error-Format: problem-details on every request to the KSeF API (see Error format), so KSeF returns its 400 and 429 errors as application/problem+json, and trace_id is set on every API error KSeF returns in that format. (401, 403 and 410 are always Problem Details.) The header is not sent to presigned storage URLs. The response body is not part of the message; it stays on response. When the body is not a recognizable error, the description is a short, truncated snippet of it.

Error format

TransportConfig.error_format chooses the format the SDK asks KSeF for:

ValueHeader sent400 and 429 errors
"problem-details" (default)X-Error-Format: problem-detailsapplication/problem+json: trace_id is set and response is BadRequestProblemDetails / TooManyRequestsProblemDetails
"legacy"noneapplication/json: no trace_id, and response is ExceptionResponse / TooManyRequestsResponse

401, 403 and 410 are always Problem Details. The SDK reads both formats the same way, so ksef_code, details, the class and the hint do not change; only trace_id and the type of response do. Use "legacy" only if your code reads response and expects the older models:

client = Client(Environment.PRODUCTION, transport_config=TransportConfig(error_format="legacy"))
python

A Problem Details body that does not match its model in the KSeF spec (or comes with a status the spec has no model for, such as 500) is not parsed: the description is a snippet of the body and response is None.

For example, downloading an invoice UPO that KSeF has not issued yet (the description and details are KSeF's own words, which are Polish):

KSeF rejected GET /sessions/online/S1/invoices/I1/upo (HTTP 400, KSeF code 21178): Nie znaleziono UPO dla podanych kryteriów.
Details: UPO o numerze referencyjnym I1 nie zostało znalezione.
Trace ID: 0b1f6a7c-4d2e-4a53-9a6e-3f2b9d1c8e11
Hint: KSeF has no UPO for this yet. Call `wait()` on the invoice submission or the session first, then call `download_upo()` again. If `wait()` raises `KSeFInvoiceRejectedError`, KSeF rejected the invoice and will never issue a UPO for it.
text

Hints

KSeFException.hint says what to do next. It is shown on its own Hint: line in str(exc) and stored in exc.context["hint"]. A hint is set only when the SDK knows the cause, every method it names is a current one, and it can be replaced for one occurrence by passing hint=... to the exception.

Hints on API errors come from the KSeF code first, then the HTTP status. A code with its own hint keeps it on a 401 or 403 too, and the class still follows the status there (KSeFAuthError):

HTTP statusKSeF codeClassHint
any21155KSeFApiErrorThe session has reached its invoice limit. Close it with close() and send the remaining invoices in a new session from online_session().
any21165KSeFNotReadyErrorKSeF has processed the invoice but has not made it available yet. Call download() with a timeout so the SDK keeps polling until it is.
any21178KSeFNotReadyErrorKSeF has no UPO for this yet. Call wait() on the invoice submission or the session first, then call download_upo() again. If wait() raises KSeFInvoiceRejectedError, KSeF rejected the invoice and will never issue a UPO for it.
any21180KSeFApiErrorThe session is already closed or KSeF is processing it, so it accepts no more invoices. Send further invoices in a new session from online_session() or batch_session().
any21182KSeFApiErrorKSeF limits how many exports can run at once. Wait for a running export to finish with wait(), then call export() again.
any21183KSeFApiErrorThe date range reaches outside the data KSeF keeps. Narrow the date range in the filters passed to search().
any21184KSeFApiErrorKSeF cannot accept invoices in this session at the moment. Retry later, or send the invoice in a new session from online_session().
any21208KSeFApiErrorKSeF cancelled the batch session because the parts were not uploaded or the session was not closed in time. Send the package again in a new session from batch_session().
any21418KSeFApiErrorContinuation tokens come from KSeF and are only valid as returned. Iterate the pager the SDK returns, or use its pages(), instead of building or reusing a token yourself.
any21470KSeFApiErrorKSeF does not know the public key the request was encrypted with, or has retired it. The SDK keeps KSeF certificates for 24 hours; create a new client so it loads the current ones.
any25006KSeFApiErrorKSeF allows only a limited number of certificate enrollments. get_limits() shows how many are still allowed.
any25007KSeFApiErrorYou hold the maximum number of KSeF certificates. Revoke one you no longer use with revoke(), and check get_limits() for the limit.
any26001KSeFApiErrorA token can only get permissions the authenticated identity holds. Request fewer permissions in generate().
any30001KSeFApiErrorThe subject or person already exists on KSeF TEST. Reuse it, or remove it first with delete_subject() or delete_person().
401otherKSeFAuthErrorKSeF rejected the credentials or the access token. Authenticate again with client.authentication.with_token() or client.authentication.with_xades(), and check that the token or certificate is valid for this context.
403otherKSeFAuthErrorThe authenticated identity is not allowed to do this in the current context. Check the reason in details, and grant the missing permission with the permissions client, for example grant_person().

KSeFRateLimitError builds its hint from retry_after, for example Wait 17 seconds before retrying.

Hints on the other SDK errors:

ClassHint
KSeFArgumentErrorPass exactly one of the mutually exclusive arguments named in the message.
KSeFAuthPollingTimeoutErrorKSeF has not finished authenticating yet. Authenticate again with a larger timeout in client.authentication.with_xades() or client.authentication.with_token().
KSeFAuthenticationExpiredErrorAuthenticate again with client.authentication.with_token() or client.authentication.with_xades(), or restore tokens that are still valid with client.authentication.resume().
KSeFBatchSessionTimeoutErrorKSeF may still be processing the batch. Call wait() on the batch session again with a larger timeout. After a restart, rebuild the session from the state saved with resume_state() using batch_session(state=...).
KSeFCertificateEnrollmentFailedErrorRead description and details for the reason, then submit a corrected request with enroll(). get_limits() shows how many enrollments and certificates are still allowed.
KSeFCertificateEnrollmentTimeoutErrorKSeF may still issue the certificate. Call wait() on the enrollment again with a larger timeout, or check it with get_enrollment_status().
KSeFExportFailedErrorRead description and details for the reason, then start a new export with export().
KSeFExportTimeoutErrorThe export may still finish. Call wait() on the export again with a larger timeout. After a restart, get the job back with export(state=...) using the state saved from resume_state().
KSeFInvoiceDownloadTimeoutErrorKSeF has not made the invoice available yet. Call download() again with a larger timeout.
KSeFInvoiceProcessingTimeoutErrorKSeF may still accept the invoice. Keep waiting with submission(reference_number).wait() on the session, with a larger timeout. After a restart, rebuild the session from the state saved with resume_state() using online_session(state=...).
KSeFInvoiceQueryTimeoutErrorNo invoice matched before the deadline. Call wait() again with a larger timeout, or check the filters passed to search().
KSeFInvoiceRejectedErrorRead description and details for the reason, fix the invoice and send it again with send_invoice().
KSeFNotReadyErrorKSeF has not finished preparing this resource. Wait and request it again.
KSeFOnlineSessionTimeoutErrorKSeF may still be processing the session. Call wait() on the session again with a larger timeout. After a restart, rebuild the session from the state saved with resume_state() using online_session(state=...).
KSeFPermissionOperationFailedErrorRead description for the reason, fix the request and grant or revoke again. get_operation_status() shows the operation's final state.
KSeFPermissionOperationTimeoutErrorThe operation may still be applied. Call wait() on the operation again with a larger timeout, or check it with get_operation_status().
KSeFTokenStatusTimeoutErrorKSeF may still be activating the token. Call wait() on the token again with a larger timeout, or check it with get_status().

KSeFSessionError has no class-level hint. The SDK sets one where it knows the cause: waiting on a session that is still open (close it first), and a session that KSeF finished with a failure (read the failed invoices). A rejected duplicate (440) gets its own hint on KSeFInvoiceRejectedError.

KSeF error codes

ksef_code is the number KSeF sent. The SDK never drops a code it does not know. Only 21165 and 21178 have a dedicated class (KSeFNotReadyError); the rest are plain KSeFApiError with ksef_code set (KSeFAuthError on a 401 or 403). The codes below are every code the KSeF API documentation (openapi.json) lists, all of them on HTTP 400 responses, and each has an ExceptionCode member. A unit test fails when a spec update adds a code that is not listed.

21178 does not always mean "not issued yet": KSeF also returns it, for good, for an invoice it rejected (for example a duplicate, status 440). Call wait() first; if it raises KSeFInvoiceRejectedError, there is no UPO to download.

KSeF codeExceptionCodeMeaning
9101INVALID_DOCUMENTInvalid document.
9102MISSING_SIGNATURENo signature.
9103TOO_MANY_SIGNATURESNumber of allowed signatures exceeded.
9105INVALID_SIGNATUREInvalid signature.
21001UNREADABLE_CONTENTUnreadable content.
21111INVALID_AUTH_CHALLENGEInvalid authorization challenge.
21115INVALID_CERTIFICATEInvalid certificate.
21117INVALID_CONTEXT_IDENTIFIERInvalid subject identifier for the context type.
21155SESSION_INVOICE_LIMIT_EXCEEDEDNumber of invoices allowed in a session exceeded.
21157INVALID_PACKAGE_PART_SIZEInvalid package part size.
21161PACKAGE_PART_LIMIT_EXCEEDEDNumber of package parts exceeded.
21164INVOICE_NOT_FOUNDAn invoice with the given identifier does not exist.
21165NOT_PROCESSED_YETThe invoice with the given KSeF number is not available yet. KSeFNotReadyError.
21166TECHNICAL_CORRECTION_UNAVAILABLETechnical correction unavailable.
21167TECHNICAL_CORRECTION_NOT_ALLOWEDThe invoice status does not allow a technical correction.
21173SESSION_NOT_FOUNDNo session with the given reference number.
21175QUERY_RESULT_NOT_FOUNDThe query result with the given identifier does not exist.
21178UPO_NOT_FOUNDNo UPO found for the given criteria. KSeFNotReadyError.
21180SESSION_STATUS_FORBIDS_OPERATIONThe session status does not allow the operation.
21181INVALID_EXPORT_REQUESTInvalid invoice export request.
21182EXPORT_LIMIT_REACHEDLimit of running exports reached.
21183FILTER_RANGE_OUT_OF_BOUNDSThe filter range is outside the available data range.
21184SESSION_TEMPORARILY_UNAVAILABLESession temporarily unavailable.
21205EMPTY_PACKAGEThe package cannot be empty.
21208UPLOAD_WINDOW_EXCEEDEDTimeout for upload or finish requests exceeded.
21217INVALID_CHARACTER_ENCODINGInvalid character encoding.
21301NO_AUTHORIZATIONNo authorization.
21304NO_AUTHENTICATIONNo authentication.
21308DECEASED_PERSON_AUTHENTICATIONAttempt to use the authorization methods of a deceased person.
21401SCHEMA_VALIDATION_FAILEDThe document does not match the XSD schema.
21402INVALID_FILE_SIZEInvalid file size.
21403INVALID_FILE_HASHInvalid file hash.
21405VALIDATION_ERRORInput validation error.
21406SIGNATURE_AUTH_TYPE_CONFLICTSignature and authentication type conflict.
21418INVALID_CONTINUATION_TOKENThe continuation token is invalid.
21470UNKNOWN_KEY_IDThe key identifier is unknown or points to a retired key.
25001CSR_DATA_UNAVAILABLECannot fetch CSR data for the authentication method used.
25002ENROLLMENT_NOT_ALLOWEDCannot submit a certificate request with the authentication method used.
25003CSR_DATA_MISMATCHThe CSR data does not match the authentication vector.
25004INVALID_CSRInvalid CSR format or signature.
25005ENROLLMENT_NOT_FOUNDNo certificate request with the given reference number.
25006ENROLLMENT_LIMIT_REACHEDLimit of certificate requests reached.
25007CERTIFICATE_LIMIT_REACHEDLimit of held certificates reached.
25008CERTIFICATE_NOT_FOUNDNo certificate with the given serial number.
25009CERTIFICATE_NOT_REVOCABLEThe certificate is already revoked, blocked or invalid.
25010INVALID_KEYInvalid key type or length.
25011INVALID_CSR_SIGNATURE_ALGORITHMInvalid CSR signature algorithm.
26001TOKEN_PERMISSIONS_NOT_HELDA token cannot get permissions you do not hold.
26002TOKEN_CONTEXT_NOT_ALLOWEDCannot generate a token for the current context type.
30001OBJECT_ALREADY_EXISTSThe entity or permission already exists.
71001COLLECTIVE_INVOICE_NOT_FOUNDAn invoice with the given identifier does not exist.
71002COLLECTIVE_IDENTIFIER_LIMIT_REACHEDThe invoice is already assigned to the maximum number of collective identifiers.
71004DIFFERENT_SELLERSThe invoices have different sellers.
71005DUPLICATE_KSEF_NUMBERA KSeF number is repeated in the request.

ExceptionCode values

ExceptionCode is an IntEnum: each member's value is its KSeF code, and the names are in the table above. One more member is not a KSeF code:

NameValue
UNKNOWN_ERROR10000

Codes the enum does not list map to ExceptionCode.UNKNOWN_ERROR, but the raw number stays on ksef_code. Prefer ksef_code in new code.

Polling timeout classes

Polling timeout exceptions mean the local wait deadline expired. They do not by themselves prove the remote KSeF workflow failed.

ClasscodeIdentifier attributes
KSeFAuthPollingTimeoutErrorAUTH_POLLING_TIMEOUTreference_number, timeout
KSeFTokenStatusTimeoutErrorTOKEN_STATUS_TIMEOUTreference_number, timeout
KSeFInvoiceQueryTimeoutErrorINVOICE_QUERY_TIMEOUTtimeout
KSeFInvoiceDownloadTimeoutErrorINVOICE_DOWNLOAD_TIMEOUTksef_number, timeout
KSeFInvoiceProcessingTimeoutErrorINVOICE_PROCESSING_TIMEOUTinvoice_reference_number, timeout
KSeFExportTimeoutErrorEXPORT_TIMEOUTreference_number, timeout
KSeFPermissionOperationTimeoutErrorPERMISSION_OPERATION_TIMEOUTreference_number, timeout
KSeFCertificateEnrollmentTimeoutErrorCERTIFICATE_ENROLLMENT_TIMEOUTreference_number, timeout
KSeFBatchSessionTimeoutErrorBATCH_SESSION_TIMEOUTreference_number, timeout
KSeFOnlineSessionTimeoutErrorONLINE_SESSION_TIMEOUTreference_number, timeout

Store the relevant reference before polling so another process can resume the status check. Every timeout error carries a hint that says how to keep waiting or resume.

download_upo() on an invoice submission, an online session and a batch session never waits and has no timeout. Call wait() on the submission or the session first. Asked too early it raises KSeFNotReadyError, with a hint that names wait(); a session that is still open raises KSeFSessionError.

Rate limit attributes

AttributeTypeMeaning
retry_after`intNone`
status_codeintAlways 429.
response`BaseModelNone`
hintstrWait <retry_after> seconds before retrying.

Invoice rejection attributes

KSeFInvoiceRejectedError subclasses KSeFSessionError, so existing handlers still catch it. Catch KSeFInvoiceRejectedError before KSeFSessionError when only a session-state violation should reopen or re-authenticate a session.

AttributeTypeMeaning
invoice_reference_numberstrReference number of the rejected invoice.
invoice_status_codeintKSeF invoice status, for example 440 or 450. Not an HTTP status.
descriptionstrKSeF status description.
detailslist[str]KSeF status details; empty when absent.
extensions`dict[str, strNone]`
statusSessionInvoiceStatusResponseThe full status response.

Unlike KSeFApiError.status_code, which is always an HTTP status, invoice_status_code comes from the KSeF invoice status object.

For a duplicate (440), extensions carries originalKsefNumber and originalSessionReferenceNumber. A caller recovering from a send whose response was lost can pass originalKsefNumber to download_invoice() and compare it against the submitted invoice. If it is the same invoice, treat the rejection as already accepted.

  • Operations reference: Review retry behavior, rate limits, workflow timeouts, and resumable state.
  • Client lifecycle: Review lifecycle errors and client close behavior.
  • Status and UPO: Understand status surfaces, UPO documents, and polling deadlines.