Volver a la wiki

Incidente #254 · La Ayuda en español mostraba el JSON de error como si fuera el artículo

Cuándo

El corpus de Ayuda en español (wiki-es/) se congeló el 2026-06-14 con 83 páginas, sincronizado 1:1 contra el corpus canónico inglés (wiki/) de ese momento. Dos artículos nuevos (“The Device page” y “Create a Row of racks”) se añadieron al corpus inglés después de esa fecha, sin gemelo español. Desde entonces y hasta el arreglo el 2026-08-23 (task #254, ronda 23-08, PR #419), cualquier usuario con la sesión en español que abriera esos dos artículos veía el fallo. Duración estimada: ~9 semanas, sin que ninguna alarma ni log lo señalara.

Síntomas visibles

El panel de Ayuda, con la interfaz en español, mostraba como contenido del artículo el volcado crudo {"error":"File not found: …"} en lugar del texto esperado. No había ningún error en los logs de aplicación (la petición HTTP al workspace devolvía 200), ninguna alarma de HighErrorRate se disparaba, y el fallo solo era visible para quien abriera esos dos artículos concretos en español.

Causa raíz

Dos fallos independientes que se combinaban:

  1. _apply_language(data, lang) (introducida en sesión 137, 2026-06-14) reescribía todas las rutas wiki/ → wiki-es/ para lang=es, sin comprobar que existiera el gemelo español. El comentario del código afirmaba “la simetría es total (82 = 82 páginas), así que el rewrite nunca da 404” — cierto en el momento de escribirlo, falso en cuanto el corpus EN creció sin que el ES lo acompañara.
  2. _mcp_call(tool_name, arguments) no distinguía contenido válido de un payload de error: el workspace MCP responde con HTTP 200 y cuerpo {"error": "..."} cuando el artículo no existe en esa ruta — resp.raise_for_status() no lo detecta porque el código de estado es 200. Ese JSON viajaba íntegro como si fuera el texto del artículo y se pintaba tal cual.

Ninguno de los dos fallos por separado bastaba para el síntoma: el primero servía una ruta wiki-es/ inexistente, y el segundo era el que convertía el 404 semántico del workspace en contenido aparentemente legítimo.

Fix aplicado

Commit 3133a0e9a613c9743d14444b248ea6db61ef676d (PR #419, v1.82.1):

  1. _mcp_call ahora detecta cuando el cuerpo es un JSON con clave "error" y lanza RuntimeError — se convierte en el 502 honesto que cada caller del Help Widget ya sabía manejar (Regla 15 — HTTP 200 ≠ éxito — aplicada en el punto exacto donde dolía; blinda el puente MCP entero, no solo el camino de artículos).
  2. _apply_language solo reescribe la ruta de un artículo cuyo título está presente en el mapa de traducción (wiki-es/titles.json) — es decir, que tiene gemelo español confirmado. Los artículos sin traducir conservan su ruta inglesa y se sirven en inglés (degradación elegante) en lugar de una ruta española rota.
  3. +3 tests en tests/api/test_help.py (test_apply_language_es_untranslated_keeps_en_path, test_mcp_call_raises_on_error_payload, test_mcp_call_passes_markdown_and_legit_json).

De regalo, en el mismo commit: las categorías “ups”/“wireless”/“settings” del panel de Ayuda (que caían al final por no estar en el array de orden de alpine-components.js) pasan a tener posición fija, y el tooltip del botón Help —que estaba en español en 5 plantillas de un producto en inglés (Regla 1)— se unifica al msgid canónico 'Help & AI assistant'.

Lecciones

Preventivos futuros

Los 3 tests nuevos cubren como regresión los dos escenarios exactos de este incidente (artículo sin gemelo ES, payload de error del MCP). Queda como mejora futura no implementada en este fix: un chequeo de contrato en CI que compare el catálogo wiki/ vs wiki-es/ y avise de asimetrías antes de desplegar, en vez de depender de que el código de aplicación degrade con gracia.

Véase también

Subir