Ir al contenido

Resolución de problemas de NextPDF Gotenberg

El puente falla de forma explícita y temprana. Cada fallo se comunica mediante una excepción tipada, y su mensaje identifica la causa. Esta página funciona como catálogo: para cada fallo indica el tipo de excepción, el fragmento de mensaje que se verá, el desencadenante exacto en la ruta de código y la solución.

Las familias de excepciones son:

  • GotenbergConvertException — fallo de la capa de conversión (configuración, transporte o respuesta).
  • RuntimeException — fallo de la capa de validación lanzado por la política de seguridad, antes de cualquier tráfico de red.
  • ValueError — una extensión de archivo no reconocida.
  • InvalidSpkiPinException — una cadena de fijación TLS (pin) con formato incorrecto.

”Invalid Gotenberg configuration: apiUrl is empty”

Sección titulada «”Invalid Gotenberg configuration: apiUrl is empty”»
  • Tipo: GotenbergConvertException
  • Desencadenante: se llamó a convertFile() o convertString() mientras GotenbergConfig::isValid() es falso. Esto ocurre cuando apiUrl es una cadena vacía.
  • Solución: proporcionar una URL HTTPS no vacía. Si la configuración se construye con fromArray(), tener en cuenta que un api_url ausente o que no sea una cadena se sustituye silenciosamente por ''. Validar el origen de la configuración durante el arranque.

Estos fallos provienen de la política de seguridad, que protege frente a la falsificación de solicitudes del lado del servidor (SSRF). Se lanzan antes de enviar cualquier solicitud. Son RuntimeException simples.

”Gotenberg API URL must use HTTPS (got: http)”

Sección titulada «”Gotenberg API URL must use HTTPS (got: http)”»
  • Desencadenante: el esquema de la URL configurada no es https. La comprobación no distingue mayúsculas de minúsculas, por lo que HTTPS:// se acepta.
  • Solución: poner Gotenberg detrás de TLS y configurar el extremo HTTPS. HTTP sin TLS se rechaza incluso en desarrollo local.

”Invalid Gotenberg API URL: unable to parse”

Sección titulada «”Invalid Gotenberg API URL: unable to parse”»
  • Desencadenante: la URL no puede analizarse para obtener un esquema y un host.
  • Solución: proporcionar una URL absoluta sintácticamente válida, por ejemplo https://gotenberg.example.com:3000.

”Gotenberg API URL must not resolve to a private or reserved IP address”

Sección titulada «”Gotenberg API URL must not resolve to a private or reserved IP address”»
  • Desencadenante: el host es un literal de IP privada o reservada, o un nombre de host que se resuelve (a través de todos los registros A/AAAA) a cualquier dirección privada o reservada. Esto bloquea los rangos RFC 1918, el bucle local (loopback) y las direcciones de enlace local (link-local).
  • Solución: apuntar el puente a una dirección de servicio pública enrutable o correctamente segmentada. Si Gotenberg está intencionadamente en una red privada, la protección SSRF del puente la rechaza por diseño. Exponerlo a través de una dirección que la protección acepte y, a continuación, proteger esa ruta con controles de red y autenticación adecuados. Consulte /integrations/gotenberg/security-and-operations/.

”Gotenberg API URL DNS answer changed since validation — possible DNS rebinding attack”

Sección titulada «”Gotenberg API URL DNS answer changed since validation — possible DNS rebinding attack”»
  • Desencadenante: entre la validación inicial y la solicitud, una nueva resolución DNS devolvió una dirección que no estaba en el conjunto validado originalmente.
  • Solución: esta es la protección de time-of-check/time-of-use en acción. Investigar el DNS del host. Una causa legítima puede ser un equilibrador de carga que rota las direcciones. Una causa maliciosa es un ataque de rebinding. Usar una dirección estable o un nombre con un conjunto de registros estable para el extremo de Gotenberg.

La política de seguridad lanza estos fallos antes de la solicitud. Salvo que se indique lo contrario, cada uno es un RuntimeException simple.

  • Tipo: GotenbergConvertException
  • Desencadenante: convertFile() no pudo canonicalizar la ruta, o la ruta resuelta no es un archivo regular legible. Un directorio también activa este fallo.
  • Solución: pasar una ruta a un archivo existente y legible. La ruta se canonicaliza primero con realpath(), lo que además neutraliza el cruce de directorios (traversal).

”File size ( bytes) exceeds maximum allowed size ( bytes)”

Sección titulada «”File size ( bytes) exceeds maximum allowed size ( bytes)”»
  • Desencadenante: la entrada supera maxFileSize (por defecto 52.428.800 bytes = 50 MiB).
  • Solución: aumentar maxFileSize si el documento lo necesita legítimamente, o rechazar la subida en una capa anterior. Mantener el límite tan bajo como permitan los documentos reales. Es el único límite de recursos integrado en el puente.

Se examina el nombre de archivo. Para las conversiones de archivos, el nombre de archivo es el nombre base de la ruta resuelta; para convertString() es el nombre proporcionado. Cada uno de estos casos es un RuntimeException:

Fragmento de mensajeDesencadenante
must not be emptynombre de archivo vacío
path traversal sequences (..)el nombre contiene ..
forward slashesel nombre contiene /
backslashesel nombre contiene \
null bytesel nombre contiene un byte NUL
control charactersel nombre contiene un carácter de control ASCII (0–31)
  • Solución: pasar un nombre base limpio. Para convertString(), proporcionar un nombre simple como report.docx. Se usa para la detección de formato y como nombre de archivo de la subida multiparte, no como una ruta.
  • Tipo: ValueError
  • Desencadenante: la extensión del archivo no es una de docx, xlsx, pptx, odt, ods, odp (sin distinción de mayúsculas/minúsculas, se tolera un punto inicial).
  • Solución: convertir solo los seis formatos reconocidos. El puente no reconoce los formatos binarios heredados (.doc, .xls, .ppt), .rtf, .csv, texto plano ni imágenes. Convertir estas entradas a un formato reconocido antes de llamar al puente, o procesarlas por otra ruta.

Todos estos fallos son GotenbergConvertException.

  • Desencadenante: el cliente PSR-18 (o el transporte con cURL fijado) lanzó una excepción al enviar la solicitud. La causa es un rechazo de conexión, un tiempo de espera agotado, un fallo del handshake TLS o una discrepancia de fijación (pin).
  • Código de excepción: el código de la excepción del cliente subyacente.
  • Causa: la excepción original del cliente PSR-18 se adjunta como la excepción previa.
  • Solución: comprobar cuatro aspectos: la accesibilidad del servicio con isAvailable(), la ruta de red, la cadena TLS y, si la fijación (pinning) está configurada, que el SubjectPublicKeyInfo (SPKI) actual del servidor coincide con un pin configurado. Una discrepancia de fijación tras una rotación de certificado es una causa habitual. Consulte el procedimiento de rotación en /integrations/gotenberg/security-and-operations/.
  • Desencadenante: el curl_exec del transporte con cURL fijado falló con un número de error de cURL distinto de cero, o devolvió un cuerpo que no era una cadena.
  • Solución: el número de error de cURL identifica la causa (TLS, resolución, tiempo de espera, fijación). Un fallo de fijación aparece aquí cuando CURLOPT_PINNEDPUBLICKEY rechaza el certificado. Confirmar que los pines configurados y la dirección resuelta están actualizados.

”Gotenberg conversion failed with HTTP : ”

Sección titulada «”Gotenberg conversion failed with HTTP : ”»
  • Desencadenante: el estado de la respuesta no fue 200. Se incluye el cuerpo, truncado a los primeros 500 caracteres, con puntos suspensivos añadidos cuando es más largo.
  • Solución: leer el cuerpo incluido. El mensaje de error de Gotenberg explica por qué se rechazó la conversión: contenido de documento no admitido, un fallo interno de LibreOffice o un rechazo de autenticación en un 401 o 403. Un 401/403 significa que apiKey falta o es incorrecta. Un 5xx es un fallo del lado del servicio y es candidato para un reintento acotado.

”Unexpected Content-Type from Gotenberg: (expected application/pdf)”

Sección titulada «”Unexpected Content-Type from Gotenberg: (expected application/pdf)”»
  • Desencadenante: el estado fue 200 pero el Content-Type de la respuesta no contenía application/pdf.
  • Solución: esto normalmente significa que un proxy o gateway devolvió una página de error HTML o de redirección con un 200. El puente desactiva deliberadamente el seguimiento de redirecciones en el transporte fijado, de modo que un 3xx no se sigue silenciosamente hasta un host no verificado. Un cuerpo que llega con el tipo equivocado es una señal de que algo entre la aplicación y Gotenberg está interfiriendo. Inspeccionar la ruta de red.

”Response body does not start with %PDF header — invalid PDF data”

Sección titulada «”Response body does not start with %PDF header — invalid PDF data”»
  • Desencadenante: estado 200, Content-Type aceptable, pero el cuerpo no comienza con la firma %PDF.
  • Solución: el origen aguas arriba devolvió algo que no es un PDF a pesar de las cabeceras. Tratar la respuesta como no fiable e investigar el servicio. No se debe escribir el cuerpo en disco. El puente se niega a devolverlo como resultado.

“Invalid SPKI pin format: (expected sha256/)”

Sección titulada «“Invalid SPKI pin format: (expected sha256/)”»
  • Tipo: InvalidSpkiPinException
  • Desencadenante: una cadena de pin configurada no empieza por sha256/ o sha256//.
  • Solución: formatear cada pin como sha256/<base64-encoded-spki-hash>. El transporte también acepta la forma nativa de cURL sha256//<base64>. Generar el valor a partir del SubjectPublicKeyInfo del certificado del servidor, no a partir del certificado completo.

”Indica que no está disponible, pero el servicio está activo”

Sección titulada «”Indica que no está disponible, pero el servicio está activo”»

isAvailable() devuelve false sin ninguna llamada de red cuando la URL está vacía, no es HTTPS o se resuelve a una dirección private/reserved. También devuelve false ante cualquier error de red, o cuando /health devuelve 500 o superior; en esos casos captura el error en lugar de lanzarlo. Comprobar, en orden:

  1. La URL configurada no está vacía y es HTTPS.
  2. El host no se resuelve a una dirección private/reserved (la protección SSRF la rechaza incluso para la sonda).
  3. <apiUrl>/health es accesible desde el host de la aplicación y devuelve un estado inferior a 500.
  • /integrations/gotenberg/configuration/ — cada opción y las reglas de selección de transporte.
  • /integrations/gotenberg/production-usage/ — política de reintentos y contrato de gestión de fallos.
  • /integrations/gotenberg/security-and-operations/ — el modelo SSRF y la rotación de fijación.
  • /integrations/gotenberg/quickstart/ — el orden de captura exhaustivo en contexto.