CreaRack-SL

El conector de Holded confundía JSON válido con HTML de error (Content-Type poco fiable)

Cuándo

21-09-2026 (s337), dos commits en 11 minutos: dc24a8ca (10:42) y f3d0c912 (10:53).

Síntomas visibles

Descubierto no por un reporte de usuario, sino al probar las fuentes de datos antes de estrenar la orden /lunes: holded_list_treasury “funcionaba” pero en realidad devolvía el HTML de una página 404 como si fuera un dato válido; holded_financial_summary contaba “2 facturas” que en verdad eran los 2 caracteres del texto []; y holded_list_invoices reventaba directamente si Holded no devolvía una lista.

Causa raíz

Dos fallos compuestos en holdedFetch (functions/api/mcp/handlers/holded.ts):

  1. La función decidía si parsear JSON fiándose de la cabecera Content-Type de la respuesta. La ruta de tesorería usada era /api/invoicing/v1/treasuries (plural), que no existe — la ruta real de Holded es singular, /treasury — y una ruta inexistente en Holded responde con el HTML de su propia página “404 · Holded” con un estado que igualmente pasa como correcto (res.ok). El código entregaba ese HTML como si fuera el dato pedido.
  2. El primer arreglo (dc24a8ca) fue al extremo contrario: rechazar cualquier respuesta que no llegara etiquetada como JSON. Pero Holded también falla al revés — sirve JSON perfectamente válido (por ejemplo, una lista vacía []) etiquetado con Content-Type: text/html. Ese arreglo estricto convertía esas respuestas legítimas en error.

Fix aplicado

  • dc24a8ca corrige la ruta de tesorería a /treasury y blinda holded_list_invoices contra respuestas que no sean una lista.
  • f3d0c912 corrige el criterio de fondo: se juzga el CUERPO de la respuesta, no la cabecera — se intenta JSON.parse del texto recibido, y solo si ese parseo falla se considera un error real (ruta inexistente o API cambiada).

Lecciones

Un Content-Type es tan poco fiable como un HTTP 200: hay que desenvolver siempre el cuerpo antes de confiar en la forma de una respuesta externa. Y un primer arreglo demasiado estricto es tan peligroso como el bug original — sustituye un falso-positivo (HTML tratado como dato) por un falso-negativo (JSON válido tratado como error). El catálogo de arquitectura del workspace ya lo señala como materialización concreta de la Regla 8 (honestidad + entrada tipada): el riesgo teórico de “200 con fallo” se dio el 21-09 de una forma distinta a la prevista.

Preventivos futuros

Tres tests nuevos en test/holded-non-json.test.ts cubren los tres casos: JSON válido etiquetado como text/html, HTML real de un 404, y lista vacía. Nota añadida a la ficha workspace-ws5-integraciones.md del Atlas de Arquitectura. Holded sigue sin configurar en producción — el fix corrige el conector, no sustituye la puesta en marcha real, que queda pendiente de Txell (task #330).

Véase también

  • [[workspace—producto—workspace-agentes]]
  • [[crearack-tech—guides—secret-rotation-playbook]]
  • [[entity—biblioteca—service—bib-openapi]]
  • [[entity—workers—middleware—api-auth]]
  • [[entity—biblioteca—handler—bib-ask]]