Registro de errores
Audience
Este artículo es para operadores de la plataforma GCM. Los administradores de iglesia deberían ver el artículo del registro de auditoría en Usuarios y roles para cambios que pueden investigar ellos mismos — el registro de errores es a nivel de plataforma e incluye trazas de pila y cuerpos de petición, que son exclusivos del personal de GCM por diseño.
La pestaña Registro de errores (/platform/error-log) es la cola central de triaje para todo lo que explota en cualquier parte del sistema. Excepciones de frontend capturadas por el límite de Sentry, edge functions que lanzan, rutas de API de Vercel que devuelven 500 y triggers de base de datos que disparan — todos convergen en platform_error_log y emergen aquí, filtrables por fuente, severidad, organización y estado resuelto, auto-actualizándose cada 30 segundos.

Las cinco fuentes
Cada fila lleva un enum source. El desplegable de filtro te permite acotar a uno a la vez:
- Frontend — excepciones de React no capturadas. Capturadas por el límite de error global y publicadas vía
/api/log-error. Los camposurlyuser_agentestán rellenados. - Edge function — edge functions de Deno que lanzaron o llamaron a
log()con severidaderror. La columnafunction_nameestá establecida. - API de Vercel — funciones serverless de Node (las rutas
/api/*). Como las edge functions pero con el contexto runtime de Vercel. - Trigger de DB — disparado desde dentro de Postgres, normalmente por un
RAISE EXCEPTIONen un trigger. La traza de pila es el contexto de error de Postgres. - Manual — cualquier cosa para la que
log_errorfue llamado explícitamente. Usado para advertencias no fatales que merece la pena emerger.
Severidad
Tres niveles, todos codificados por color:
warning— ámbar. No fallan las peticiones, pero suficientemente ruidosos para investigar. Denegaciones de RLS, consultas lentas, llamadas reintentadas al gateway.error— rosado. Una petición falló. El usuario vio algo roto.fatal— rojo profundo. Una petición falló de una forma que dejó el estado inconsistente. Trátalo como prioridad uno.
El filtro por defecto es "no resuelto"
La página carga con resolved = false para que la cola sea lo que está abierto. Cambia a Resueltos para ver historial triado, o Todos para una vista completa. El filtro Org tiene una opción especial Sin atribuir (sin organización) para filas que se dispararon antes de que se estableciera un contexto de inquilino (el caso más común para errores de arranque del frontend).
La búsqueda de texto golpea cuatro columnas a la vez: message, function_name, url, error_code. Úsala como usarías la búsqueda de Sentry — coincidencia parcial, insensible a mayúsculas.
Cómo se ve una fila
Cada fila de la tabla muestra:
- Cuándo — relativo ("hace 3m"). Pasa el ratón para la marca de tiempo completa.
- Fuente — ícono coloreado + etiqueta.
- Severidad — píldora.
- Org — derivado de
context.org_namecuando está presente, si no los primeros 8 caracteres deorganization_id, si no un guion. - Mensaje — el mensaje de error, truncado.
- Dónde —
function_namesi está disponible, de lo contrariourl. - Estado — Abierto o Resuelto.
Haz clic en cualquier fila para abrir el diálogo de detalle.
El diálogo de detalle
El diálogo de detalle es el espacio de trabajo de triaje. La mitad superior es una cuadrícula clave/valor: función / URL, código de error, usuario (nombre + correo), org + plan, los roles que tenía, si era administrador de plataforma en el momento, ruta, contexto de acción, id de correlación, user agent.
Siguen dos secciones colapsables:
- Traza de pila — la pila cruda del runtime. Para triggers de DB este es el contexto de error de Postgres. Para errores de frontend es la pila de JS, con source-map cuando es posible.
- Contexto (completo) — la columna JSONB
contextcompleta, formateada legiblemente. Aquí es donde las edge functions guardan cuerpos de petición, parámetros de consulta y valores intermedios. Trátalo como sensible — puede incluir correos e IDs.
PII en el contexto
La columna context no está redactada. Es un volcado crudo de lo que sea que el código fallido capturó. No la pegues en Slack, en un ticket público ni en un issue de Sentry. El registro de errores vive detrás de la compuerta del administrador de plataforma precisamente para que esto sea seguro.
Resolviendo una fila
La parte inferior del diálogo tiene los controles de resolución:
- Marcar como resuelto — captura una nota opcional ("arreglado en #123" o "transitorio, ignorando") y estampa
resolved,resolved_at,resolved_by,resolved_note. La fila sale de la vista por defecto de no resueltos. - Reabrir — solo visible en filas ya resueltas. Limpia las columnas de resolución.
- Eliminar — destructivo. Elimina la fila individual por completo. Útil para exposiciones de datos personales que no deberían permanecer en la base de datos.
La resolución es una señal de flujo de trabajo, no una solución. Marcar una fila como resuelta no detiene el bug subyacente — solo le dice a tu yo futuro que el error ha sido triado.
Acciones masivas
Dos operaciones masivas viven en la cabecera de la tabla:
Purgar resueltos > 30d
Elimina permanentemente filas resueltas con más de 30 días. Esta es la barrida de retención — una fila triada no merece quedarse más allá de un mes. Barato de ejecutar, seguro de ejecutar regularmente. El botón reporta la cantidad que removió.
Limpiar filtrados
Elimina cada fila que coincida con el conjunto de filtros actual. Esto es peligroso y la interfaz lo sabe:
- El botón está deshabilitado a menos que al menos un filtro reduzca el conjunto. Sin un filtro, la copia "matching the current filters" del diálogo sería una mentira.
- El diálogo de confirmación muestra el conteo exacto que eliminará.
Úsalo para limpiar una clase ruidosa de advertencias después de haber lanzado el arreglo: filtra a source = frontend, message = ChunkLoadError, resolved = unresolved, presiona Limpiar, mira el conteo bajar a cero.
Qué significa la auto-actualización
La consulta vuelve a buscar cada 30 segundos vía el refetchInterval de React Query. El spinner de carga aparece en línea junto al conteo de filas en cada refetch — si está girando para siempre, la red está atascada y las filas existentes están obsoletas. El botón Actualizar fuerza una invalidación inmediata.
Patrones comunes
Un pico de advertencias EDGE_FUNCTION de una organización — usualmente un webhook mal configurado golpeando un endpoint que devuelve 429. Abre la organización y verifica sus filas channels.
Errores fatales sin organización — la falla pasó antes de que se resolviera el contexto de inquilino. Crashes de arranque del frontend, errores del auth-hook y fallos de arranque en frío de edge-fn se agrupan aquí. Agrupa por mensaje y la causa suele ser una variable de entorno faltante.
Errores de denegación RLS de db_trigger — alguien está llamando una consulta sin el rol correcto. Cruza el id de usuario en context con el registro de auditoría para ver qué estaban tratando de hacer.
Si una sola fila necesita ser investigada por un ingeniero que no es administrador de plataforma, copia el correlation_id y comparte eso — pueden hacer grep en los logs de edge-function en Supabase con el mismo id sin que tú tengas que reenviar la fila.
