Errores y códigos de estado
Toda falla que el SDK lanza a propósito es un DteError con un code estable. Tú decides según el código y lees el contexto desde details; nunca interpretas el mensaje. Esta página lista cada código, qué lo lanza y qué suelen traer sus details, y luego cubre los códigos de estado del SII (envío, recepción, documento, reclamos y cedibilidad), que se devuelven como dato y no se lanzan.
Usar DteError#
import { createDocument, DteError, errorCode, isDteError } from 'dte-sdk'
try {
createDocument({ ...input, items: [{ name: 'x'.repeat(81), unitPrice: 1 }] })
} catch (error) {
if (isDteError(error)) {
error.code // 'FIELD_TOO_LONG'
error.message // 'Field exceeds the maximum length allowed by the SII schema (field=NmbItem, maxLength=80, length=81)'
error.details // { field: 'NmbItem', maxLength: 80, length: 81, value: 'xxxxxxxx…' }
}
}| Export | Qué hace |
|---|---|
DteError | La clase de error. code es un DteErrorCode, details es un registro de solo lectura con contexto. |
isDteError(value) | Guarda de tipo: true cuando value es un DteError. |
errorCode(value) | El código cuando value es un DteError, si no undefined. Útil cuando reportas fallas como dato. |
DTE_ERROR_CODES | El objeto con todos los códigos: DTE_ERROR_CODES.INVALID_RUT === 'INVALID_RUT'. |
DteErrorCode, DteErrorDetails | Los tipos del código y de los detalles. |
isRetryableError(value) | True para los errores que merecen otro intento. Ver Reintentos. |
details frente a message#
El mensaje es genérico y seguro de registrar: es una frase fija por código, seguida de una lista corta de claves de detalle seguras, es decir, nombres de campo, códigos y números. Nunca incluye RUT, nombres, texto de ítems, URL ni cuerpos de respuesta. Las claves seguras son field, maxLength, length, type, folio, from, to, kind, index, status, httpStatus, code, attempts, channel, operation, trackId, expected, actual, rejection, step, module, day, listed y covered.
Todo lo demás vive solo en details: el valor ofensivo, un RUT, un extracto de respuesta (con tokens y semillas tachados), la URL que se llamó. Trata details como sensible: no lo envíes completo a un log ni a un rastreador de errores. Elige las claves que necesitas (error.details.field, error.details.status).
logger.error('upload failed', { code: error.code, status: error.details.status }) // bien
logger.error('upload failed', error.details) // puede traer RUT, un extracto de respuesta, el valor ofensivoCódigos de error#
Los códigos están agrupados por tema. Los "details típicos" son las claves que el código suele traer; una operación puede agregar más (todo error de transporte trae además operation).
Validación de entradas#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
INVALID_RUT | Un RUT está mal formado o su dígito verificador es incorrecto. Lo lanza toda función que recibe un RUT. | field |
INVALID_DOCUMENT_TYPE | El tipo de documento no está soportado. También: un tipo fuera de 33, 34 y 43 en el registro de reclamos, fuera de BOOK_DOCUMENT_TYPES en un libro, fuera de 39 y 41 en un reporte de consumo de folios, una no guía en guideEntryFromDocument. | field, type, index |
INVALID_FOLIO | Un folio no es un entero positivo (libros: hasta 10 dígitos). En un reporte de consumo de folios, un folio repetido entre las líneas. | field, folio, type, index |
INVALID_DATE | Una fecha no es AAAA-MM-DD, un período no es AAAA-MM, o una fecha de libro está fuera de 2000 a 2050. | field |
INVALID_AMOUNT | Un monto no es un número finito (libros: no es un entero seguro, o es negativo donde el esquema lo prohíbe). | field, index |
MISSING_FIELD | Falta un campo u opción obligatorios: por ejemplo companyRut en una llamada sin valor por defecto en el cliente, cover.notificationFolio en un libro especial. | field |
FIELD_TOO_LONG | Un texto excede el largo máximo del esquema del SII (por ejemplo 80 caracteres para el nombre de un ítem). | field, maxLength, length, value |
INVALID_OPTION | Una opción tiene un valor inválido: un environment o channel desconocido, un timeout no positivo, un trackId incorrecto, una semilla que no son dígitos, un fileName no seguro, una zona de libro que no calza con su envío… | field, a veces index |
ENCODING_UNSUPPORTED_CHAR | Un texto contiene un carácter fuera de ISO-8859-1, así que no se puede enviar ni timbrar. | index, codePoint |
CAF y folios#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
CAF_INVALID | El XML del CAF no se puede leer o no es lo que debe ser un CAF. parseCaf y verifyCafSignature también rechazan un DOCTYPE, y parseCaf un texto con más de un <CAF o </CAF y un CAF cuya llave privada no es el par de su clave pública (reason: 'RSASK is not the pair of RSAPK'). Una FRMA vacía ya no es CAF_INVALID: verifyCafSignature responde invalid. | reason |
CAF_EXPIRED | El CAF pasó los 6 meses desde su fecha de autorización; se lanza al timbrar. | type, authorizationDate |
CAF_TYPE_MISMATCH | El CAF es de otro tipo de documento que el que estás timbrando. | type, cafType |
CAF_ISSUER_MISMATCH | El CAF pertenece a otro RUT emisor que el del documento. | type |
FOLIO_OUT_OF_RANGE | El folio está fuera del rango del CAF, o un registro de folios o el asignador de folios de certificación se quedan sin folios. | folio, from, to, type |
Certificado#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
CERT_INVALID | El PKCS#12 o el PEM no se puede leer, no trae llave privada o certificado, o la llave no corresponde al certificado. | reason |
CERT_PASSWORD_WRONG | La contraseña del PKCS#12 es incorrecta. | ninguno |
CERT_EXPIRED | El certificado venció. | expiresAt |
CERT_NOT_YET_VALID | El certificado aún no está vigente: su notBefore todavía no llega. | validFrom |
XML y firmas#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
XML_PARSE_FAILED | Un XML no se puede interpretar, un elemento a firmar no tiene ID, o falta la raíz o la etiqueta de cierre esperada. verifyReceivedDocuments también la lanza para un DOCTYPE, un sobre sin exactamente un SetDTE y un DTE o Documento con prefijo. Todo DOM que parsea el SDK rechaza con ella los elementos anidados a más de 100 niveles, antes de que algo los recorra. | field, reason, root, line, column |
SIGNATURE_NOT_FOUND | Se verifica una firma en un XML que no tiene Signature (o SignedInfo). | field |
ENVELOPE_EMPTY | Un sobre no tiene documentos. | kind o field |
ENVELOPE_KIND_MISMATCH | Un documento no corresponde a este tipo de sobre (una boleta en un EnvioDTE, o una factura en un EnvioBOLETA). | kind, type |
BOOK_TOTAL_MISMATCH | Con strictTotals, el total explícito de una línea de libro difiere de la fórmula del SII. | index, expected, actual |
Transporte#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
TRANSPORT_UNAVAILABLE | No hay fetch en el runtime, o un adaptador de Node no pudo cargar node:https o node:fs. | reason |
TRANSPORT_FAILED | La petición falló antes de que llegara una respuesta (DNS, conexión rechazada o reiniciada, una petición que no se puede armar). | reason, operation, responseStarted |
TRANSPORT_TLS | Falló el handshake TLS o la verificación del certificado. | reason (el código de OpenSSL o del SO), operation |
REQUEST_TIMEOUT | Venció el timeout de la llamada. | operation, timeoutMs, reason |
REQUEST_ABORTED | Se disparó el AbortSignal del llamador. | operation |
Cuando una falla de transporte ocurre después de que llegaron las cabeceras de la respuesta (mientras se leía el cuerpo), details.responseStarted es true: el SII sí respondió, al menos en parte.
Servicios web del SII#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
SII_HTTP_ERROR | El SII respondió con un estado HTTP que no es 2xx. | httpStatus, operation, channel, url, body (extracto, tachado), retryAfterMs |
SII_RESPONSE_INVALID | La respuesta no se puede interpretar: no es el XML o JSON que devuelve el canal, o le falta el elemento o código esperado. | operation, body |
SII_SOAP_FAULT | El SII respondió con un fault SOAP. | code, reason (tachado), operation |
SII_SEED_FAILED | El SII se negó a entregar una semilla. | code, channel |
SII_TOKEN_FAILED | El SII se negó a entregar un token para una semilla firmada. | code, channel |
SII_TOKEN_INVALID | El SII rechazó el token. El cliente lo renueva y repite la llamada una vez; esto sale solo si el token nuevo también se rechaza. | operation, channel, httpStatus o status o code, attempts |
SII_UPLOAD_REJECTED | El SII rechazó un envío: un estado de DTEUpload distinto de recibido o duplicado, o más de 500 boletas en el canal REST. | status (un número, o 'TOO_MANY'), errors, fileName, operation, message, trackIdElement |
SII_QUERY_ERROR | Una consulta de estado respondió un código negativo -1 a -14: falló el servicio de consulta, no tu sobre. | operation, code, reason |
WAIT_TIMEOUT | waitForUploadStatus agotó su presupuesto de tiempo con el estado aún pendiente. | trackId, attempts, lastCode |
Portal#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
SII_PORTAL_LOGIN_FAILED | El inicio de sesión no entregó sesión. | operation, portalMessage |
SII_PORTAL_CERT_NOT_ENABLED | El certificado no está habilitado en el SII como método de ingreso. | operation, portalMessage |
SII_PORTAL_SESSION_LIMIT | El SII informa demasiadas sesiones autenticadas para el RUT. | operation |
SII_PORTAL_SESSION_EXPIRED | El portal sigue pidiendo autenticación. | operation, step |
SII_PORTAL_BLOCKED | El firewall del SII o un captcha bloqueó la petición. | operation, step, httpStatus, portalMessage |
SII_PORTAL_REJECTED | El portal entendió la solicitud y la rechazó. details.rejection dice por qué. | rejection, operation, más extras como portalMessage, file, httpStatus |
SII_PORTAL_UNEXPECTED_PAGE | El portal respondió una página que no se puede interpretar. | operation, step, portalMessage, body |
Dependencias opcionales#
| Código | Se lanza cuando | Details típicos |
|---|---|---|
DEPENDENCY_UNAVAILABLE | Una función de dte-sdk/pdf necesita pdf-lib o bwip-js y no está instalado. | module, reason |
PDF_RENDER_FAILED | La biblioteca de PDF falló al dibujar una muestra impresa. | reason, type, folio |
Reintentos#
isRetryableError(error) es true para:
| Error | Condición |
|---|---|
TRANSPORT_FAILED, REQUEST_TIMEOUT | siempre |
SII_RESPONSE_INVALID | siempre (una página ilegible suele ser una falla pasajera del servicio) |
SII_QUERY_ERROR | siempre |
SII_HTTP_ERROR | httpStatus 429, 405 o 5xx |
SII_UPLOAD_REJECTED | status 9 ("sistema bloqueado") |
Es false para todo lo demás, incluidos TRANSPORT_TLS (un almacén de confianza mal configurado no se arregla solo) y SII_TOKEN_INVALID (el cliente ya renueva el token una vez). El cliente del SII la usa como valor por defecto de retry.retryOn, con backoff exponencial y seis intentos. Dos cosas que recordar:
- Un envío o un reclamo no se reintenta solo porque el error sea reintentable. Esas llamadas cambian estado en el SII y se reenvían únicamente mientras no haya llegado respuesta.
SII_UPLOAD_REJECTEDcon estado 9 es "reintentable" según esta función, pero un envío que recibió esa respuesta nunca se reenvía automáticamente: lo decides tú. Ver la política de reintentos. - El portal tiene su propia regla,
isRetryablePortalErrordedte-sdk/portal: fallas de transporte, timeouts, HTTP 502, 503 y 504, y las fallas TLS intermitentes que el SII produce con un certificado cliente (EPROTO). Un límite de sesiones, un bloqueo o un rechazo nunca se repite, porque repetir los empeora. Su política,DEFAULT_PORTAL_RETRY_POLICY, es de 4 intentos a los 2, 4 y 8 segundos, sin jitter.
Rechazos del portal#
Un SII_PORTAL_REJECTED es el portal rechazando una solicitud por motivos de negocio: la empresa no está inscrita, se agotó el cupo, el respaldo está bloqueado. details.rejection es un código PortalRejection. portalRejectionMessage(rejection) y el mapa PORTAL_REJECTIONS, ambos de dte-sdk/portal, entregan la explicación completa en español, escrita para el operador, con qué hacer al respecto.
import { isDteError } from 'dte-sdk'
import { portalRejectionMessage, requestCaf, type PortalRejection } from 'dte-sdk/portal'
try {
await requestCaf(portal, { type: 33, quantity: 50 })
} catch (error) {
if (isDteError(error) && error.code === 'SII_PORTAL_REJECTED') {
console.error(portalRejectionMessage(error.details.rejection as PortalRejection))
console.error(error.details.portalMessage) // lo que dijo el portal, cuando dijo algo
} else {
throw error
}
}Los códigos, por área:
| Área | rejection | Significado (breve) |
|---|---|---|
| Acceso | inPersonRequired | El representante legal debe completar la inscripción en persona en una oficina del SII. |
companyNotAuthorized | La empresa no está autorizada como emisor electrónico en este ambiente. queryAuthorizedIssuer lo lanza para un RUT que no figura en el registro público. | |
optionNotAuthorized | El SII dice que la opción no está autorizada, sin indicar la causa; lo más frecuente es que la empresa aún no esté autorizada. | |
userWithoutPermission | El RUT del certificado no tiene permisos en la empresa en este ambiente, o el SII dice que el usuario no está autorizado para solicitar timbraje ("No está autorizado para solicitar timbraje"). | |
personWithPendingIssues | La persona autenticada tiene situaciones pendientes con el SII. | |
activityVerificationPending | El RUT no registra verificación de actividades para este tipo de documento. | |
notEnrolled | La empresa no está inscrita en la postulación a factura electrónica, así que no se puede descargar el set de pruebas. | |
| Folios y CAF | stampingBlocked | El SII no autoriza timbraje: hay folios suficientes sin usar. |
typeNotEnabled | El SII no ofrece este tipo de documento para timbraje con este RUT. | |
quotaInsufficient, quotaExceeded, quotaCapReached, foliosShort | El SII autoriza menos folios de los pedidos, o la solicitud excede el máximo. | |
noAnnullableFolios | El timbraje está limitado y no quedan folios anulables. | |
stampingUncertain | El SII volvió a mostrar el formulario de solicitud sin entregar folios ni decir por qué, así que no se sabe si autorizó el rango. La solicitud no se envía de nuevo: revisa los rangos que se pueden reobtener antes de pedir otra vez. requestCafInBatches lo informa como tal. | |
rangeAlreadyAuthorized, rangeAnnulled | El rango ya fue autorizado (se puede reobtener) o está anulado. | |
reobtainFormMissing, reobtainDownloadMissing, reobtainCafMissing | El SII no devolvió el formulario, la descarga o el CAF al reobtener un rango. Una página de ingreso en su lugar es SII_PORTAL_SESSION_EXPIRED. | |
| Certificación | advanceRefused, declarationDeclined | El portal no avanzó la postulación o no ofreció el formulario de declaración. |
exchangeUploadRefused | El portal de intercambio rechazó un archivo de respuesta (details.file). | |
samplesReviewRefused, sampleUploadRefused | El portal de muestras impresas no creó la revisión, o rechazó un PDF con un estado 4xx. | |
receiptsNotEnrolled, receiptsTrackIdRefused, receiptsComplianceRefused | La postulación de boletas rechazó: sin inscripción, el track id del set o la declaración de cumplimiento. | |
| Respaldo MIPYME | backupCaptcha | El SII activó un captcha. Intentar más tarde, nunca en un loop. |
backupIndeterminate | La página dio varios mensajes y ningún veredicto único. Intentar más tarde. | |
backupNoData | El SII informa que no hay información para este RUT. Un veredicto definitivo. | |
backupRefused, backupNoCompany | El portal rechazó, o respondió la página de selección de empresa sin mensaje. | |
backupDayOverLimit, backupDayUnknownTypes | Un solo día supera los 20 documentos por descarga y no se puede dividir. No se descargó nada. | |
| Registro | purchaseChangeNotConfirmed | El SII no confirmó un cambio en el registro de compras, pero pudo haberse aplicado: relee el documento antes de reintentar. |
| Verificación de actividades | activityVerificationRefused | El SII respondió la verificación de actividades con un código de error; sus palabras van en details.portalMessage cuando dijo algo. |
Ver Portal y Registro para las funciones que hay detrás.
Códigos de estado#
El SII informa los resultados con códigos cortos. El SDK los devuelve como dato (no son excepciones), los clasifica y entrega su glosa en español. Las glosas están en español porque son el vocabulario propio del SII.
import {
classifyDocumentStatus,
classifyUploadStatus,
documentStatusMessage,
isRetryableStatus,
isTokenErrorStatus,
uploadStatusMessage,
} from 'dte-sdk'
classifyUploadStatus('EPR') // 'accepted'
classifyUploadStatus('RCT') // 'rejected'
classifyUploadStatus('-11') // 'queryError': reintentar
classifyUploadStatus('RLV') // 'rejected' en SOAP…
classifyUploadStatus('RLV', 'rest') // …y 'accepted' en la API REST de boletas
classifyUploadStatus('XYZ') // 'unknown'
classifyDocumentStatus('FAU') // 'notReceived'
uploadStatusMessage('EPR') // 'Envío procesado'
documentStatusMessage('FAU') // 'DTE no recibido por el SII'
isRetryableStatus('-11') // true
isTokenErrorStatus('-3') // true: renueva el token y prueba una vez másEstados de envío#
El ESTADO de QueryEstUp (o el estado REST) de un sobre o un libro. classifyUploadStatus(code, channel = 'soap') devuelve una de estas clases; uploadStatusMessage(code, channel) la glosa, o Estado desconocido: <code>.
| Clase | Códigos |
|---|---|
accepted | EPR (envío procesado), LOK (envío de libro aceptado) |
acceptedWithObjections | RPR (aceptado con reparos) |
pending | REC (recibido, esperando validación), SOK (schema validado, validando firma), FOK (firma validada, procesando), PRD (en proceso, validando carátula), CRT (carátula validada, finalizando), DNK (en proceso de revisión), PDR (en proceso, validando), LSO (schema de libro correcto, procesando contenido), 0 (enviado, pendiente de validación) |
rejected | RPT (rechazado por schema), RFR (error en firma), VOF (error interno del SII), RCT (error en carátula), RCH (rechazado), RCO (envío rechazado), RSC (error en schema), RLV (error en documento; éxito en REST), LNC (tipo de envío de libro no corresponde), LRH (libro rechazado con historial) |
queryError | -1 a -14: fallas del servicio de consulta, no de tu archivo. -3 y -13 significan token rechazado. |
unknown | Cualquier otro. |
isRetryableStatus(code) es true para la clase queryError. isTokenErrorStatus(code) es true para -3, -13, 001, 002 y 003: el cliente renueva el token una vez cuando ve uno.
Estados de recepción#
El STATUS de la respuesta RECEPCIONDTE que entrega DTEUpload cuando recibe (o rechaza) un archivo. RECEPTION_STATUS es el catálogo, classifyReceptionStatus(code) la clase y receptionStatusMessage(code) la glosa. SII_UPLOAD_REJECTED.details.status es este código como número.
STATUS | Clase | Significado |
|---|---|---|
0 | received | Envío recibido, TrackID asignado. |
1 | rejected | El remitente no tiene permiso para enviar por esta empresa. |
2 | rejected | Error en el tamaño del archivo (muy grande o muy chico). |
3 | rejected | Archivo cortado o incompleto. |
5 | notAuthenticated | No autenticado: token ausente o vencido. El cliente renueva el token y envía de nuevo una vez. |
6 | rejected | Empresa no autorizada a enviar archivos. |
7 | schemaInvalid | Esquema inválido o archivo ya enviado (ver DETAIL). El cliente los distingue: los errores de esquema o de charset (CHR-…, SCH-…, cvc-…) son un rechazo; si no, con un TrackID en la respuesta, es un duplicado. |
8 | rejected | Error en la firma del documento. |
9 | rejected | Sistema bloqueado, reintentar más tarde. El único que se resuelve solo. |
99 | alreadyUploaded | Archivo ya fue enviado anteriormente. Un duplicado cuando la respuesta nombra el TrackID. |
Estados de documento#
El ESTADO de QueryEstDte (o el codigo REST) de un documento. classifyDocumentStatus(code) y documentStatusMessage(code); cualquier código negativo es queryError, y cualquier otro no listado es unknown.
| Clase | Código | Glosa |
|---|---|---|
received | DOK | Documento recibido por el SII, datos coinciden |
receivedWithMismatch | DNK | Documento recibido por el SII, datos no coinciden |
notReceived | FAU | DTE no recibido por el SII |
notAuthorized | FNA | DTE no autorizado |
EMP | Empresa no autorizada a emitir DTE | |
modified | TMD, TMC | Existe nota de débito o de crédito que modifica textos |
MMD, MMC | Existe nota de débito o de crédito que modifica montos | |
cancelled | AND, ANC | Existe nota de débito o de crédito que anula el documento |
FAN | Folio anulado |
Códigos de reclamo y de cedibilidad#
El registro responde con un número codResp. claimResponseMessage(code) entrega la glosa (Código de respuesta desconocido: <code> para un código no listado), y CLAIM_RESPONSE_CODES es el catálogo.
codResp | Glosa |
|---|---|
0 | Acción completada OK |
1 | RUT del emisor erróneo |
2 | Folio erróneo |
3 | El tipo de documento no corresponde |
4 | Acción inválida |
5 | DTE ya reclamado |
6 | No se puede otorgar recibo de mercaderías a un DTE reclamado |
7 | Evento registrado previamente |
8 | Han pasado más de 8 días desde la recepción del documento |
9 | No existen registros para los parámetros indicados |
10 | DTE no recibido en el SII desde el 14-01-2017 |
11 | No se puede reclamar un DTE aceptado previamente |
12 | No se puede aceptar un DTE rechazado previamente |
13 | No se puede reclamar un DTE con recibo de mercaderías |
14 | Solo la empresa receptora o emisora puede consultar |
15 | Lista de eventos del documento |
16 | El documento no tiene eventos registrados |
17 | Solo la empresa receptora puede registrar eventos |
18 | Documento no recibido en el SII |
19 | Reclamo por mercaderías ya realizado |
27 | No se puede registrar un evento de un DTE pagado al contado o gratuito |
-1 | Error interno del SII, reintentar |
queryTransferable responde con los códigos 20 a 25. transferabilityMessage(code) entrega la glosa, y TRANSFERABILITY_CODES el catálogo, con lo que implica cada uno:
codResp | transferable | Glosa |
|---|---|---|
20 | false | Tipo de documento no cedible |
21 | false | No cedible: anulado por nota de crédito dentro de los 8 días |
22 | null | Sin registro de reclamo ni de recibo de mercaderías |
23 | true | Cedible, sin reclamos registrados |
24 | false | No cedible: reclamado por el receptor |
25 | true | Cedible: transcurridos 8 días sin reclamo, se presume recibido |
Los eventos que informa listClaimEvents (CLAIM_EVENT_CODES): ACD (acepta contenido), RCD (reclamo al contenido), ERM (otorga recibo de mercaderías o servicios), RFP (reclamo por falta parcial), RFT (reclamo por falta total), NCA (nota de crédito de anulación), ENC (otra nota de crédito) y PAG (DTE pagado al contado, que registra el propio SII).
El código 27 es el registro rechazando un evento sobre un documento pagado al contado o gratuito: producción lo respondió el 11 de noviembre de 2026 a la aceptación de una factura cuyos eventos listaban solo PAG. receiverAnswer lee un documento así como paidInCash, así que no hay nada que enviar (revisa receiverAnswer). Antes de la 0.8.0 el catálogo no lo conocía, y claimResponseMessage(27) daba Código de respuesta desconocido: 27.
Próximos pasos#
- Servicios web del SII muestra de dónde vienen estos errores y estados, y cómo funcionan los reintentos y la renovación de token.
- Portal explica la sesión, los límites y los rechazos del portal.
- Seguridad dice qué mantener fuera de tus logs.