Manejo de errores y normalización de mensajes

Cómo PayCal estandariza los informes de error en todos los módulos frontend para que los usuarios reciban retroalimentación útil, segura y coherente sin exponer detalles sensibles.

Resumen y propósito

Cuando los usuarios encuentran errores, merecen una respuesta clara que explique qué ocurrió y cómo corregirlo. Los mensajes de error brutos del backend deben normalizarse para:

  • Eliminar ruido: quitar prefijos redundantes "Error:" y limpiar espacios
  • Prevenir fugas: asegurar que los detalles de implementación sensibles nunca lleguen al usuario
  • Proporcionar alternativas: mostrar mensajes seguros cuando los errores están vacíos o mal formados
  • Asegurar coherencia: aplicar la misma lógica en los más de 11 módulos frontend
  • Mejorar la depuración: registrar detalles completos en Phantom Wing mientras se muestran resúmenes seguros a los usuarios

El problema: errores genéricos frente a mensajes útiles

Antes de la estandarización, los módulos de PayCal usaban manejo de errores ad hoc:

// ❌ MAL: expone el error bruto, duplica lógica
PC.showToast(error?.message || 'Importación fallida.');
PW.error(`Importación fallida: ${error.message}`);

Problemas de este enfoque:

  • Los usuarios ven mensajes confusos como "ECONNREFUSED: Conexión rechazada"
  • Cada módulo implementa su propia lógica de respaldo de forma independiente
  • No hay limpieza consistente de espacios ni eliminación de prefijos
  • Los mensajes de error vacíos pueden mostrarse como "undefined" en la UI

La solución: resolvedor de errores estandarizado

Todos los módulos frontend de PayCal usan ahora una función resolvedora unificada que normaliza los mensajes de error:

// ✅ BIEN: normalizado, coherente, seguro
const resolveThrownMessage = (error, fallbackMessage) => {
  const raw = typeof error?.message === 'string' 
    ? error.message 
    : String(error || '');
  const normalized = raw.replace(/^Error:\s*/i, '').trim();
  return normalized !== '' ? normalized : fallbackMessage;
};

Uso:

// En bloques catch en todos los módulos
try {
  await updateProfile(data);
} catch (error) {
  const message = resolveThrownMessage(error, 'No se pudo actualizar el perfil.');
  PC.showToast(message, 'error');  // El usuario ve retroalimentación útil
  PW.error(message);                // Registrado para depuración
}

Alcance de implementación

A abril de 2026, este patrón estandarizado de manejo de errores se ha aplicado a 11 módulos frontend con aproximadamente 40+ bloques catch normalizados:

Autenticación y ajustes (7 módulos)

  • html/js/auth-recovery/index.php (4 catches)
  • html/js/signin/index.php (2 catches)
  • html/js/signin/verification-reminder.js (2 catches)
  • html/js/signin/verification-status-banner.js (1 catch)
  • html/js/settings/index.php (8+ catches)

Módulos core y de datos (4 módulos)

  • html/js/core/network.js (3 catches)
  • html/js/core/index.php (5 catches)
  • html/js/core/billing.js (5 catches)
  • html/js/earnings/index.php (4 catches)

Módulos de alto valor (10+ puntos de captura):

  • html/js/organizations/index.php - gestión de organizaciones, solicitudes de acceso, trazas de auditoría (19+ catches)
  • html/js/sites/index.php - CRUD de sitios, ganancias, recuperación de trabajo huérfano (10+ catches)
  • html/js/calendar/calendar.js - operaciones de entrada diaria, copiar/pegar/eliminar (2 catches)

Categorías de error y patrones de manejo

El resolvedor se aplica de forma consistente en varias categorías de error:

1. Fallos de solicitudes de red

// Módulo de red: errores HTTP, tiempos de espera, problemas de conexión
async function deleteResource(ep, id) {
  try {
    // ...lógica fetch...
  } catch (error) {
    const resolved = resolveThrownMessage(error, 'Error de red');
    const msg = `[deleteResource] ${resolved}`;
    PW.error(msg);
    throw new Error(msg);
  }
}

2. Manejo de respuestas API

// Facturación/Ajustes: el servidor devolvió un mensaje de error en la carga útil
try {
  const response = await fetch('/api/v1/billing/subscription');
  const payload = await response.json();
  if (!response.ok) {
    throw new Error(payload?.message || 'No se pudo cargar el estado de facturación.');
  }
} catch (error) {
  const resolved = resolveThrownMessage(error, 'No se pudo cargar el estado de facturación.');
  setScreenReaderStatus(resolved);
}

3. Fallos de operaciones de UI

// Calendario/Organizaciones: acciones iniciadas por el usuario (pegar, eliminar, actualizar)
button.addEventListener('click', async () => {
  try {
    await performAction();
    PC.showToast('¡Éxito!', 'save');
  } catch (error) {
    const message = resolveThrownMessage(error, 'La acción falló. Inténtalo de nuevo.');
    PC.showToast(message, 'error');
  }
});

4. Inicialización asíncrona

// Módulos core: fallos de inicio o de inicialización dependiente
try {
  NavigationToggle.init();
} catch (err) {
  const resolved = resolveThrownMessage(err, 'Falló la inicialización de navegación');
  PW.warn(resolved);  // Se registra pero no bloquea la página
}

Consideraciones de seguridad

La normalización de mensajes de error protege la privacidad del usuario y la integridad del sistema:

  • Sin detalles de base de datos: errores backend como "UNIQUE constraint failed on email" se interceptan en la frontera de la API y se reemplazan por mensajes amigables
  • Sin rutas de archivo: los errores del sistema que exponen rutas de archivo o detalles de procesos se eliminan
  • Sin filtración de autenticación: las respuestas a fallos de autenticación nunca revelan si una cuenta existe (solo mensajes genéricos seguros ante temporización)
  • Sin detalles CORS/red: los errores de transporte se normalizan a mensajes genéricos "Error de conexión"
  • Respaldo seguro: todos los catchers tienen mensajes de respaldo explícitos; nunca muestran "undefined" o "null"

Beneficios para la experiencia de usuario

Esta estandarización ofrece a los usuarios retroalimentación más clara, segura y coherente, al tiempo que facilita el soporte y la depuración para el equipo.

  • Los mensajes son comprensibles sin exponer detalles de implementación
  • El texto de respaldo sigue siendo seguro cuando el error del backend falta o está mal formado
  • El registro sigue siendo útil para depuración sin filtrar valores sensibles a la UI

Flujo de depuración y soporte

El soporte puede confiar en un camino de resolvedor consistente y, cuando sea necesario, revisar los registros de Phantom Wing para el detalle técnico completo.

  • El usuario ve un mensaje normalizado
  • Phantom Wing registra en privado el contexto completo del error
  • El soporte puede correlacionar la clase de problema sin exponer la respuesta bruta del backend

Pruebas y aseguramiento de calidad

La lógica de normalización está cubierta por pruebas focalizadas de módulo y por comprobaciones de integración en los principales flujos frontend.

Mantenimiento y extensiones futuras

A medida que PayCal agregue más módulos, se debe reutilizar el mismo patrón de resolvedor en lugar de duplicar un formato de error personalizado.

  • Los módulos nuevos deben llamar directamente al resolvedor compartido
  • Los mensajes de respaldo específicos del módulo deben seguir siendo breves y orientados al usuario
  • Cualquier cambio en el resolvedor debe probarse en regresión con flujos representativos

Eso mantiene el comportamiento predecible a medida que crece la base de código y evita una segunda generación de manejo de errores específico por módulo.

Resumen: el estándar de manejo de errores de PayCal

PayCal estandarizó el manejo de errores frontend para que los usuarios obtengan mensajes seguros y coherentes, mientras los desarrolladores siguen recibiendo la información que necesitan para depurar.

  • Normalizar los mensajes de error antes de mostrarlos
  • Ocultar los detalles de implementación a los usuarios
  • Usar texto de respaldo explícito para errores vacíos o mal formados
  • Registrar en privado los detalles técnicos para soporte y depuración