Errores de validación de KSeF FA(3): guía para desarrolladores
Diagnostica errores de validación de KSeF FA(3) distinguiendo fallos de XML, semántica, duplicados, autenticación y transporte para reintentar con seguridad.
Ernest Bursa
Un error de KSeF FA(3) no es un único tipo de error. Puede ser un fallo HTTP, una excepción síncrona de la API, un estado asíncrono de la factura, un estado de la sesión o un estado de autenticación. Para diagnosticarlo, combina tres datos: la operación, el contexto del estado y el código. Después, corrige los fallos deterministas, concilia los duplicados y los envíos de resultado incierto, y reintenta solo los fallos que de verdad sean transitorios.
Esta guía se refiere al contrato 2.6.1 de la API de producción de KSeF, disponible el 28 de agosto de 2026. TEST ya ofrece la versión 2.7.1, así que consulta siempre la especificación OpenAPI de producción y los límites de tu entorno de ejecución antes de dar por permanente cualquier tabla. Esta es una guía de ingeniería, no asesoramiento fiscal ni jurídico.
¿Qué se considera un error de validación de KSeF FA(3)?
La aceptación en KSeF es un proceso, no una única comprobación del XSD. Una factura enviada mediante una sesión online puede pasar por todas estas etapas:
- Tu cliente serializa la factura comercial como XML FA(3).
- Calcula el hash y cifra exactamente los bytes que se enviarán.
- KSeF acepta o rechaza la solicitud a la API de forma síncrona.
- Una solicitud aceptada entra en el procesamiento asíncrono de facturas.
- KSeF verifica el archivo, el cifrado, los permisos, la identidad de posibles duplicados y determinadas reglas semánticas.
- Una factura procesada correctamente recibe un número KSeF y pasa a ser apta para obtener un UPO.
La primera distinción útil es la que separa el éxito del transporte del éxito de la factura. POST /sessions/online/{referenceNumber}/invoices devuelve HTTP 202 Accepted con una referencia de factura. Eso solo significa que el procesamiento ha empezado. No significa que el XML haya superado la validación, que la factura haya recibido un número KSeF ni que exista un UPO.
Guarda la referencia de factura devuelta antes de hacer cualquier otra cosa. Consulta el resultado de la factura y conserva el de la sesión como una señal independiente. La especificación OpenAPI de producción incluso muestra una sesión con estado 200 que contiene diez facturas: ocho se procesaron correctamente y dos fallaron. Una sesión correcta no demuestra que todas sus facturas hayan salido bien.
El error inverso también es habitual: un HTTP 200 de un endpoint de estado significa que la consulta se ha completado, no que la factura sea correcta. El cuerpo de la respuesta puede contener el estado de procesamiento 440, 450 u otro fallo.
¿Por qué un código de KSeF no significa nada sin su contexto?
Imagina que una línea del registro solo dice KSeF error 440. Aún no sabes qué ha ocurrido.
- El estado de factura
440significa que la factura está duplicada. - El estado de sesión
440significa que la sesión se canceló, por ejemplo, tras agotarse el tiempo o porque no contenía facturas. - El estado de factura
450indica un fallo de validación semántica. - El estado de autenticación
450indica que el token no es válido.
Estos valores no forman un registro global de números de error. Pertenecen a modelos de respuesta concretos. Un registro de diagnóstico útil necesita el contexto que rodea al número.
| Campo que se debe conservar | Por qué importa |
|---|---|
| Entorno y versión de la API | TEST y producción pueden ejecutar contratos distintos |
| Operación o endpoint | Identifica qué contexto de estados corresponde |
| Estado HTTP | Separa la gestión de la solicitud o el transporte del resultado del procesamiento |
| Código de excepción o procesamiento | Clasifica el fallo dentro de ese contexto |
Descripción, details y extensions
|
Contiene los diagnósticos prácticos del servidor y las referencias originales |
| Referencias de sesión y factura | Permite reanudar las consultas y conciliar resultados inciertos |
| Hash y tamaño exactos del XML | Vincula una respuesta con los bytes que pretendías enviar |
Asigna a ese registro un identificador interno de correlación y añádelo a cada reintento y consulta. Así, un panel puede agrupar los fallos sin borrar las pruebas necesarias para reproducir uno concreto.
Mantén también separadas las excepciones síncronas de la solicitud y los estados asíncronos de la factura. En el envío online, las excepciones HTTP 400 actuales incluyen un estado de sesión no válido (21180), una discrepancia de tamaño (21402), una discrepancia de hash (21403) y un fallo de validación de la solicitud (21405). Si solicitas X-Error-Format: problem-details, los errores de solicitud compatibles pueden utilizar el formato estructurado Problem Details. Ninguno de estos códigos debe mezclarse en un gráfico de estados de factura.
¿Qué estados de factura exigen esperar, corregir, conciliar o reintentar?
Utiliza el estado que devuelve el endpoint de estado de cada factura, no el estado HTTP de esa solicitud GET.
| Estado de factura | Significado en producción 2.6.1 | Acción predeterminada |
|---|---|---|
100, 150
|
Aceptada para continuar el procesamiento / en proceso | Esperar y volver a consultar con una espera progresiva |
200 |
Procesada correctamente | Registrar el número KSeF; obtener y guardar el UPO |
405 |
Cancelada debido a un error de sesión | Examinar primero el fallo de la sesión |
410 |
Ámbito de permisos no válido | Corregir la autorización; no modificar el XML a ciegas |
415 |
No se puede enviar una factura con adjuntos | Corregir la autorización para adjuntos o el formato de la factura |
430 |
Fallo al verificar el archivo de la factura | Comprobar los bytes, el XML, el esquema, los límites, el hash y las reglas relacionadas con el archivo |
435 |
Fallo de descifrado | Corregir la gestión de la clave y del cifrado |
440 |
Factura duplicada | Conciliarla con la sesión y el número KSeF originales |
450 |
Fallo de validación semántica | Corregir los datos de la factura a partir de la información devuelta |
500 |
Estado interno desconocido | Conservar el diagnóstico y conciliar antes de iniciar una recuperación limitada |
550 |
Procesamiento cancelado internamente | Conciliar y después reintentar con una política limitada |
Esta tabla sirve para decidir la ruta de gestión, no para sustituir la respuesta. Conserva la descripción original, todos los campos details y cualquier extensions. El Ministerio no publica un catálogo estable y exhaustivo que asocie cada posible detalle de 430 o 450 con un XPath. El código debe admitir estados nuevos o desconocidos en lugar de convertir las descripciones actuales en un analizador frágil.
El estado de la sesión sigue siendo importante, pero por otra razón. Un error de archivo, descifrado, tiempo de espera o paquete en la sesión puede cancelar sus facturas. Cuando se procese la sesión, revisa los recuentos de éxitos y fallos y, después, el estado de cada factura. El endpoint de facturas fallidas es la vía más rápida para obtener diagnósticos en un lote con resultados distintos.
¿Cómo debes comprobar el XML FA(3) antes de enviarlo?
Desde el 1 de febrero de 2026, FA(3) es el único esquema de factura estructurada que se acepta para envíos nuevos, incluidas las correcciones de facturas emitidas originalmente con FA(1) o FA(2). Abre la sesión con systemCode: "FA (3)", schemaVersion: "1-0E" y value: "FA", y valida el documento contra el XSD oficial de FA(3).
Valida exactamente los bytes cuyo hash vas a calcular y que vas a cifrar. La guía de verificación de facturas del Ministerio exige XML 1.0, UTF-8 sin marca de orden de bytes, el esquema declarado al abrir la sesión, ninguna declaración de codificación incompatible, ninguna instrucción de procesamiento y ninguno de los rangos Unicode desaconsejados que especifica. Si incluyes una estructura opcional, sus campos hijos obligatorios pasan a ser necesarios.
En una integración con Rails que use Nokogiri, puedes empezar la comprobación local así:
schema = Nokogiri::XML::Schema(File.read("schemat_FA(3)_v1-0E.xsd"))
bytes = File.binread("invoice.xml")
raise "UTF-8 BOM is not allowed" if bytes.start_with?("\xEF\xBB\xBF".b)
document = Nokogiri::XML(bytes) { |config| config.strict.nonet }
errors = schema.validate(document)
raise errors.map(&:message).join("\n") if errors.any?
Esto detecta XML mal formado e infracciones del XSD. No reproduce todas las validaciones del servidor. Añade como mínimo estas comprobaciones en tu aplicación:
- la identidad del vendedor y el número de factura que tu sistema de numeración pretende utilizar;
- las fechas, en especial que
P_1no sea posterior a la aceptación de KSeF; - las estructuras condicionales de FA(3) y los cálculos contables;
- los límites de tamaño de archivo y número de facturas por sesión;
- la autorización para adjuntos, cuando corresponda;
- los tamaños en bytes y hashes SHA-256 del contenido en claro y cifrado enviados en los metadatos;
- el cifrado con la clave pública vigente de KSeF y los algoritmos documentados.
Conserva la validación de las reglas comerciales aunque KSeF devuelva 200. Las preguntas y respuestas sobre KSeF del Ministerio explican que el sistema puede aceptar una factura con errores aritméticos o con un NIP de la contraparte incorrecto pero cuya suma de comprobación sea válida. La aceptación del servidor demuestra que KSeF ha aceptado la factura estructurada, no que tus datos contables sean correctos.
TEST, DEMO y producción tampoco demuestran lo mismo. TEST utiliza datos anonimizados y no tiene efectos jurídicos; DEMO emplea autenticación real, pero tampoco tiene efectos jurídicos; producción sí los tiene. Algunas comprobaciones, incluidas determinadas validaciones de la suma de comprobación del NIP, solo se aplican en producción. Superar las pruebas en TEST demuestra que tu integración funciona en TEST, no garantiza que las supere en producción.
¿Cómo puedes diagnosticar el estado 450 sin hacer conjeturas?
Trata 450 como una conclusión del servidor sobre la semántica de la factura y conserva suficientes datos de entrada para reproducirla exactamente. No empieces modificando campos al azar hasta que desaparezca el error.
- Guarda el objeto de estado completo, incluidos todos los detalles que devuelva KSeF.
- Localiza la instantánea inmutable de los datos de origen utilizada para generar la factura.
- Comprueba que el hash del XML guardado coincide con los bytes enviados bajo esa referencia de factura.
- Vuelve a ejecutar los validadores locales de XSD y reglas comerciales con esa instantánea.
- Asocia el detalle devuelto al campo FA(3) y al valor del sistema de origen que lo generó.
- Corrige el origen o el mapeador, genera un XML nuevo y valida los bytes nuevos desde el principio.
Un XML rechazado no se ha emitido. El Ministerio indica que debe corregirse y enviarse un XML válido; no se trata de una factura rectificativa de una factura aceptada. Esta distinción importa a la hora de diseñar los reintentos: la corrección genera un nuevo intento de envío, aunque la identidad contable y el registro de auditoría deben seguir vinculados al intento fallido.
Si la misma carga útil supera TEST pero falla en producción, investiga las diferencias de autorización, permisos, identidad y validación entre entornos antes de relajar un validador local. No envíes nunca una factura desechable a producción solo para ver qué ocurre: si se acepta, tendrá efectos jurídicos.
¿Por qué el estado de duplicado 440 exige conciliación?
KSeF identifica un duplicado mediante tres campos comerciales: el NIP del vendedor (Podmiot1:NIP), el tipo de factura (RodzajFaktury) y el número de factura (P_2). El periodo de unicidad documentado dura diez años naturales completos después de que termine el año en el que se emitió la factura.
Por tanto, 440 no demuestra que los bytes del XML sean idénticos. Significa que KSeF ya ha aceptado una factura con esa identidad contable. El estado puede incluir originalSessionReferenceNumber y originalKsefNumber; utilízalos.
La ruta de recuperación es la siguiente:
- Busca el número KSeF y la referencia de sesión originales en las extensiones del estado.
- Compara la factura original con la transacción de origen prevista.
- Obtén y verifica el UPO original.
- Marca el intento local como conciliado con esa factura aceptada.
- Escala el caso si la factura aceptada no representa la transacción comercial prevista.
No incrementes P_2 solo para que desaparezca el error. Si la solicitud original se completó, pero se perdió su respuesta, cambiar el número puede crear una segunda factura con efectos jurídicos. Separa la numeración comercial de los intentos de transporte: una factura puede tener varios intentos registrados, pero un reintento no debe inventar otro documento comercial a escondidas.
Esto también deja al descubierto una carrera importante. Si varios equipos o unidades emisoras comparten un mismo NIP de vendedor, deben coordinar la numeración de las facturas. La unicidad local dentro de cada aplicación no basta para la clave de duplicados global de KSeF.
¿Qué fallos de KSeF se pueden reintentar con seguridad?
La política de reintentos parte de la capa en la que se produjo el fallo.
No reintentes sin cambios los fallos deterministas de los datos de entrada. Los fallos de XML o XSD, las discrepancias de tamaño o hash, los problemas de permisos, la falta de autorización para adjuntos, los fallos de descifrado y el estado semántico 450 exigen una corrección. Enviar los mismos bytes en las mismas condiciones solo generará ruido, consumirá límites y no aportará información nueva.
No reintentes a ciegas el estado de duplicado 440. Concílialo con la factura original aceptada.
Ante un HTTP 429, respeta por completo Retry-After. Los límites de frecuencia de KSeF utilizan ventanas superpuestas de un segundo, un minuto y una hora. Repetir solicitudes mientras existe un bloqueo puede alargarlo. Coordina los procesos que compartan el mismo contexto de autenticación y la misma IP, añade un retardo aleatorio antes de liberar el trabajo en cola y consulta GET /rate-limits durante la ejecución en lugar de asumir que los límites publicados se aplican a tu cuenta.
Ante tiempos de espera agotados y HTTP 5xx, el resultado puede ser incierto. La respuesta puede perderse después de que el servidor haya confirmado la solicitud. La especificación OpenAPI de producción no documenta ninguna clave de idempotencia proporcionada por el cliente para el envío online ni para el cierre de lotes. Por tanto, lo siguiente es una recomendación de ingeniería, no una garantía de KSeF:
- guarda la referencia de sesión, el hash y el tamaño de la factura, y la fecha y hora del intento antes de enviarla;
- después de un envío online de resultado incierto, examina la misma sesión y concilia su lista de facturas antes de repetirlo;
- después de un cierre de lote incierto, consulta esa sesión y vuelve a cerrarla solo si sigue abierta;
- utiliza una espera exponencial limitada con retardo aleatorio después de comprobar que no existe un resultado confirmado;
- detente al alcanzar el presupuesto de reintentos y envía el intento a revisión humana sin perder sus pruebas.
El estado de factura 550 indica expresamente que se vuelva a intentar, pero que algo sea «reintentable» no significa que deba reintentarse sin fin. Conserva el diagnóstico, concilia el intento y aplica la misma política de recuperación limitada. Para el estado 500, no confundas un código del procesamiento de la factura con HTTP 500: conserva el contexto e investiga antes de decidir.
Las consultas de estado requieren la misma moderación. Continúa con 100 y 150, aumenta la espera entre consultas y detente al alcanzar un estado terminal. El bucle fijo de un segundo que utiliza un cliente de ejemplo no constituye un SLA oficial de procesamiento.
¿Qué pruebas debe conservar una integración en producción?
Cuando una factura se procesa correctamente, las pruebas son más que un estado verde en un panel. Conserva un registro duradero que conecte la transacción comercial con lo que KSeF aceptó:
- una instantánea inmutable de los datos de origen y la versión del mapeador o del esquema;
- el hash exacto y el tamaño en bytes del XML en claro;
- los metadatos de cifrado y el hash y tamaño del contenido cifrado;
- el entorno y la versión de la API observados;
- los números de referencia de la sesión y la factura;
- el historial de estados con fecha y hora, descripciones, detalles y extensiones;
- el número de factura KSeF;
- el XML del UPO y su valor de integridad SHA-256/Base64;
- los vínculos entre la factura comercial, todos los intentos de envío y el resultado aceptado.
El UPO de una factura solo está disponible cuando esa factura se procesa correctamente. Puede obtenerse mientras la sesión continúa abierta. Los UPO agregados de una sesión aparecen después del cierre e incluyen el subconjunto de facturas aceptadas, por lo que una sesión con resultados distintos puede tener un UPO y facturas fallidas al mismo tiempo. No utilices «la sesión tiene UPO» como equivalente de «todas las facturas se han procesado correctamente».
KSeF puede exponer una URL temporal de descarga del UPO en una respuesta de estado. Esa URL caduca y no es el documento duradero. Descarga el XML firmado, verifica el valor x-ms-meta-hash que devuelve el endpoint autenticado y conserva el documento conforme a tu política de pruebas.
Cómo gestiona KSeF Kit el ciclo de envío
KSeF Kit es un producto independiente para equipos que presentan facturas de Stripe al KSeF polaco. Su flujo de envío documentado respeta las mismas fronteras que recomienda esta guía: captura una instantánea de los datos de origen, los mapea a FA(3), realiza el envío mediante una sesión online cifrada, consulta el resultado, registra por separado cada intento de envío, reanuda la espera desde las referencias guardadas, almacena el UPO y escribe el número KSeF en Stripe.
Esto no elimina la necesidad de entender un rechazo. Le da un lugar duradero dentro de un flujo con estado en vez de dejarlo atrapado en una solicitud HTTP fallida. Los equipos que desarrollen su propia integración pueden aplicar el mismo diseño: entradas inmutables, intentos explícitos, referencias que permitan reanudar el proceso, gestión de estados según la operación y conciliación antes de repetir un envío.
Si Stripe es tu fuente de facturas y prefieres operar ese flujo en lugar de desarrollarlo, consulta cómo conecta KSeF Kit los distintos entornos y su documentación para resolver problemas. En cualquier caso, la regla para producción es la misma: un código sin su operación no es un diagnóstico, y un reintento sin conciliación no es un plan de recuperación.
Artículos relacionados
¿Listo para contratar de forma más inteligente?
Empieza gratis durante 30 días. Cancela antes de que termine y no pagas nada. Configura tu primer pipeline de contratación en minutos.
Empieza gratis