> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Solución de problemas

> Documentación de solución de problemas de AgentEye.

Esta guía mapea los síntomas más frecuentes en producción hacia diagnósticos y soluciones concretas, para que puedas resolver incidentes con las herramientas que ya tienes, sin necesidad de montar infraestructura de observabilidad adicional. Cubre el servidor, el colector, el dashboard, el asistente de IA, el SDK de Python, el monitoreo de salud y certificados, las copias de seguridad, el análisis con ClickHouse y la multi-tenencia.

Las páginas del dashboard tienen alcance de organización bajo `/<org-slug>/…`, y el stream de eventos es la página principal de la organización (`/<org-slug>/`). Los nombres de página en esta guía (por ejemplo `/sessions`, `/queries`) se refieren a esas rutas con alcance de organización.

***

## Ver logs

AgentEye no incluye una pila de logging ni monitoreo. Tanto el servidor como el dashboard escriben logs estructurados en **stdout**, por lo que puedes leerlos directamente con `kubectl` o `docker`; no se necesita ningún agregador.

### Kubernetes

Sigue los logs en tiempo real del servidor y del dashboard:

```bash theme={null}
kubectl logs -n agenteye -l app=server    -f --timestamps
kubectl logs -n agenteye -l app=dashboard -f --timestamps
```

Variantes útiles:

| Objetivo                             | Comando                                                           |
| ------------------------------------ | ----------------------------------------------------------------- |
| Últimas 200 líneas (sin seguimiento) | `kubectl logs -n agenteye -l app=server --tail=200 --timestamps`  |
| Logs del crash anterior              | `kubectl logs -n agenteye <pod-name> --previous`                  |
| Seguir todas las réplicas a la vez   | `kubectl logs -n agenteye -l app=server --max-log-requests=10 -f` |
| Postgres (StatefulSet)               | `kubectl logs -n agenteye postgres-0 -f`                          |

### Docker Compose

```bash theme={null}
docker logs -f agenteye-server
docker logs -f agenteye-dashboard
```

### Correlacionar una solicitud individual entre dashboard y servidor

Cada solicitud del dashboard se etiqueta con un `request_id` y se propaga al servidor mediante la cabecera `x-request-id`. El servidor lo incluye en sus cabeceras de respuesta y en cada línea de log que emite para esa solicitud. Para rastrear una solicitud de extremo a extremo:

1. Captura el id desde la cabecera de respuesta, por ejemplo:
   ```bash theme={null}
   curl -i https://dashboard.example.com/api/events | grep -i x-request-id
   # x-request-id: 9a7b0d6e-5e9b-4c0a-9f8a-5f1e4b5c0f3a
   ```
2. Filtra los logs de ambos pods por ese id:
   ```bash theme={null}
   REQ=9a7b0d6e-5e9b-4c0a-9f8a-5f1e4b5c0f3a
   kubectl logs -n agenteye -l app=dashboard --tail=5000 | grep "$REQ"
   kubectl logs -n agenteye -l app=server    --tail=5000 | grep "$REQ"
   ```

Verás las líneas `proxy passthrough`, `withAuth: authorized` y `upstream response` del dashboard junto con el par `http request received` / `http request completed` del servidor, todos compartiendo el mismo `request_id`.

### Logs JSON y `jq`

Configura `AE_LOG_JSON=1` en el dashboard (está activado por defecto cuando `NODE_ENV=production`) para emitir un objeto JSON por línea. Luego filtra de forma estructurada:

```bash theme={null}
kubectl logs -n agenteye -l app=dashboard --tail=5000 \
  | jq 'select(.level == "warn" or .level == "error")'

kubectl logs -n agenteye -l app=dashboard --tail=5000 \
  | jq 'select(.route == "POST /api/keys")'
```

El servidor en Rust emite pares de trazabilidad `key=value` que funcionan bien con grep sin necesidad de `jq`:

```bash theme={null}
kubectl logs -n agenteye -l app=server --tail=5000 | grep 'status=5'   # 5xx
kubectl logs -n agenteye -l app=server --tail=5000 | grep 'actor_key_id='
```

### Aumentar el nivel de verbosidad

| Componente | Variable de entorno | Ejemplo                                                  |
| ---------- | ------------------- | -------------------------------------------------------- |
| Servidor   | `RUST_LOG`          | `RUST_LOG=debug` o `RUST_LOG=agenteye_server=debug,info` |
| Dashboard  | `AE_LOG_LEVEL`      | `AE_LOG_LEVEL=debug`                                     |

`debug` en el servidor añade una línea `api key authenticated` por cada autenticación. `debug` en el dashboard añade líneas `upstream request`, `session validated` y `proxy passthrough`.

### Retención de logs

El stdout del contenedor es efímero; kubelet rota los archivos de log (por defecto \~10 MiB por contenedor) y conserva un número reducido en disco. Una vez que el pod se elimina, los logs desaparecen. Si necesitas retención prolongada o búsqueda entre pods, apunta tu clúster a un colector de logs (Loki, CloudWatch, Cloud Logging, Datadog, etc.) que siga `/var/log/containers/`. AgentEye no requiere ni prescribe ninguna opción específica.

***

## Problemas de autenticación

### `docker pull` falla con "unauthorized"

Asegúrate de haber autenticado Docker contra GHCR con tu `AGENTEYE_TOKEN`:

```bash theme={null}
echo $AGENTEYE_TOKEN | docker login ghcr.io -u x --password-stdin
```

El token debe tener permiso `read:packages` en la organización `agenteye-enterprise`. Contacta a `support@exosphere.host` si tu token no funciona.

### `gh release download` devuelve 404 o 401

* Confirma que `AGENTEYE_TOKEN` está exportado en tu shell: `echo $AGENTEYE_TOKEN`
* Confirma que estás usando `GITHUB_TOKEN=$AGENTEYE_TOKEN gh release download ...` (la CLI `gh` lee `GITHUB_TOKEN`)
* El token necesita `contents:read` en `agenteye-enterprise/releases`

***

## Problemas del servidor

### El servidor falla con "invalid port number"

El `POSTGRES_PASSWORD` (u otra credencial) contiene caracteres especiales de URL (`/`, `+`, `=`) que rompen el análisis de `DATABASE_URL`. Regenera la contraseña usando codificación hexadecimal:

```bash theme={null}
NEW_PASS=$(openssl rand -hex 24)
```

Luego actualiza el secret de Kubernetes y la contraseña en Postgres (o recrea el `.env` para Docker Compose) y reinicia el servidor. Consulta los pasos completos en [enterprise-docs/kubernetes-deployment.md](/es/agenteye/kubernetes-deployment) § "PostgreSQL credentials".

### El servidor se cierra inmediatamente al arrancar

Revisa los logs del contenedor:

```bash theme={null}
docker logs agenteye-server
```

Causas frecuentes:

* `DATABASE_URL` no está configurado o está mal formado: el servidor registrará el error y saldrá.
* Postgres no es accesible: confirma que el contenedor de Postgres o la base de datos gestionada está en ejecución y que el host/puerto son correctos.
* Las migraciones fallaron: revisa los logs en busca de errores SQL.

### `GET /health` devuelve un código distinto de 200 o agota el tiempo de espera

Es posible que el servidor aún esté ejecutando migraciones en el primer arranque. Espera unos segundos y vuelve a intentarlo:

```bash theme={null}
curl http://localhost:8080/health
```

Si el problema persiste, revisa `docker logs agenteye-server` en busca de errores.

### `GET /ready` devuelve 503

`/ready` es la sonda de preparación: devuelve `503` cuando el servidor no puede alcanzar **Postgres o ClickHouse**. El cuerpo indica la dependencia que falla:

```bash theme={null}
curl -s http://localhost:8080/ready
# {"status":"not_ready","checks":{"postgres":"ok","clickhouse":"down","redis":"ok"}}
```

Corrige la dependencia que aparece como `down`: ¿el pod de ClickHouse/Postgres está en estado `Running`? ¿Son correctos y accesibles `CLICKHOUSE_URL` / `DATABASE_URL`? En Kubernetes el pod aparece como `NotReady` hasta que `/ready` se recupera; eso es lo esperado y es exactamente la señal sobre la que alerta el monitoreo de salud. Redis nunca es la causa: se reporta pero no falla la preparación.

### El colector devuelve 401 Unauthorized

La clave API del colector no tiene permiso `events:add`, o la clave ha sido desactivada. Crea una nueva clave con el permiso correcto:

```bash theme={null}
curl -s -X POST http://your-server:8080/keys \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"new-collector","permissions":["events:add"]}'
```

### Las solicitudes autenticadas se volvieron lentas de repente (\~200ms en lugar de \~5ms)

Este es el síntoma de que Redis está caído mientras `REDIS_URL` está configurado. Cada llamada a la caché agota el tiempo de espera después de 100ms y recurre a Postgres; en las rutas de autenticación y OTP la solicitud realiza dos de estas recaídas.

Confirma en los logs del servidor:

```
auth cache: L2 get failed error=redis call timed out
```

Resolución:

1. `redis-cli -h <your-redis> ping` para confirmar que Redis es accesible en la red del clúster.
2. Si Redis estuvo caído momentáneamente y ya volvió, **reinicia los pods del servidor**. El `redis::aio::ConnectionManager` no restablece la conexión de forma fiable después de que la conexión subyacente se interrumpe; un reinicio del pod toma la nueva conexión limpiamente. Lo mismo aplica al dashboard.
3. Si por ahora no quieres ejecutar Redis, elimina `REDIS_URL` del despliegue y reinicia. Ambos servicios funcionan sin la caché (la corrección se preserva; la latencia vuelve a la línea base previa a Redis).

### El servidor reporta `OTP request rate-limited` en los logs pero el usuario dice que solo lo intentó una vez

Verifica si Redis era inaccesible. La ruta de respaldo usa `SELECT COUNT(*) FROM otp_codes WHERE created_at > now() - interval '15 minutes'`, que ve filas de OTP generadas anteriormente. Si el usuario ha estado haciendo clic repetidamente en "Reenviar" durante una hora, la ventana de 15 minutos puede contener aún ≥5 códigos. Resuélvelo esperando a que la ventana expire o ejecutando `DELETE FROM otp_codes WHERE user_id = $1 AND created_at > now() - interval '15 minutes'` (consola del operador).

### Cambié `ALLOWED_EMAILS` / `SESSION_TTL_SECS` / `OTP_TTL_SECS` y reinicié; no pasó nada

Estas variables de entorno son **valores iniciales de primer arranque únicamente**. Una vez que la tabla `settings` tiene una fila para la clave correspondiente, esa fila es la fuente de verdad; la variable de entorno se lee una sola vez en el primer arranque y luego se ignora en cada reinicio posterior.

Para cambiarlas después del primer arranque, inicia sesión en el dashboard y edítalas en `/settings`. El cambio se aplica en segundos en todas las réplicas; no es necesario reiniciar.

Si necesitas forzar una re-inicialización desde la variable de entorno (raro, generalmente útil solo en desarrollo), ejecuta `DELETE FROM settings WHERE key = '<key>'` y reinicia el servidor. El proceso de arranque tomará el valor actual de la variable de entorno en el siguiente inicio. Editar via `/settings` es la ruta soportada en producción.

***

## Problemas del colector

### El colector arranca pero los eventos no aparecen en el dashboard

1. Confirma que el colector está en ejecución: `systemctl status agenteye-collector` (Linux) o revisa el proceso.
2. Confirma que `AGENTEYE_URL` apunta a `http(s)://your-server-host:8080/events` (nota: la ruta `/events`).
3. Ejecuta un flush único para ver la salida inmediata:
   ```bash theme={null}
   agenteye-collector flush
   ```
4. Verifica que el SDK de Python realmente está escribiendo archivos: `ls ${AGENTEYE_HOME:-~/.agenteye}/events/`
5. Si hay archivos en `${AGENTEYE_HOME:-~/.agenteye}/failed/`, las subidas están fallando. Revisa los logs del colector para ver el error, probablemente un 4xx (clave o URL incorrectos) o un problema de red.

### Los archivos se acumulan en `$AGENTEYE_HOME/events/` y no se suben

* Es posible que el colector no esté en ejecución. Inícialo: `agenteye-collector start`; automáticamente envía los eventos pre-existentes al arrancar.
* Verifica el estado del colector: `agenteye-collector health`
* El colector puede estar en ejecución pero sin poder llegar al servidor. Revisa las reglas del firewall entre los hosts del colector y el servidor.

### Archivos en `$AGENTEYE_HOME/failed/`

Los archivos se mueven a `failed/` después de agotar todos los reintentos (por defecto: 5 intentos con retroceso exponencial). Esto significa que:

* El servidor devolvió un error 4xx (clave incorrecta, URL incorrecta o problema con el payload)
* El servidor no fue accesible durante toda la ventana de reintentos

Corrige el problema subyacente y luego vuelve a encolar manualmente:

```bash theme={null}
mv ${AGENTEYE_HOME:-~/.agenteye}/failed/*.jsonl ${AGENTEYE_HOME:-~/.agenteye}/events/
agenteye-collector flush
```

### El colector reporta `network error` en cada subida (el handshake TLS falla)

Si `curl -k` contra `AGENTEYE_URL` tiene éxito pero el binario del colector falla en cada subida con `error sending request for url (...)`, el servidor AgentEye está presentando un certificado TLS que no está firmado por una CA de confianza pública.

La **ruta de producción** es el hostname de ingesta ACME configurado en `deploy/base/certificates/domain.env` (ver [`kubernetes-deployment.md`](/es/agenteye/kubernetes-deployment) Fase 3.1 / 4.2). Una vez que `INGEST_DOMAIN` resuelve al LB público de Traefik y cert-manager ha emitido el certificado de Let's Encrypt, los colectores verifican el certificado del servidor contra el almacén de confianza del sistema **sin necesidad de `AGENTEYE_TLS_CA`**; elimínalo de tu configuración del colector si se configuró para un despliegue anterior con certificado autofirmado.

**Síntoma: el colector funcionaba ayer, falla hoy después de \~90 días.** Esto significa que el despliegue sigue usando el emisor `selfsigned` heredado para `ingest-tls`. El certificado de 90 días rotó y el archivo de CA fijado está desactualizado. Corrige esto de forma permanente cambiando el clúster al emisor ACME (Fase 3.1 de la guía de despliegue). Solución temporal: vuelve a extraer el certificado actual del servidor y actualiza `AGENTEYE_TLS_CA`:

```bash theme={null}
kubectl get secret ingest-tls-cert -n agenteye \
  -o jsonpath='{.data.tls\.crt}' | base64 -d > /etc/agenteye/server-ca.crt
```

```bash theme={null}
export AGENTEYE_TLS_CA=/etc/agenteye/server-ca.crt
agenteye-collector flush
```

`AGENTEYE_TLS_CA` añade un ancla de confianza adicional; las raíces públicas estándar siguen siendo de confianza.

### El certificado `ingest-tls` está atascado en `Ready: False` después del despliegue

```bash theme={null}
kubectl describe certificate ingest-tls -n agenteye
```

Revisa los `Events` y el `Order` / `Challenge` referenciado. Causas frecuentes:

* **DNS no resuelve al LB público.** El validador HTTP-01 no puede alcanzar `INGEST_DOMAIN`. Verifica con `dig +short INGEST_DOMAIN`; debería resolver a la misma dirección que el `EXTERNAL-IP` del LoadBalancer `traefik-public`. cert-manager reintenta automáticamente una vez que el DNS se propaga; no es necesario eliminar el Certificate.
* **Puerto 80 bloqueado en el balanceador de carga / grupo de seguridad.** HTTP-01 requiere que el puerto 80 sea accesible desde los validadores públicos de Let's Encrypt. Si tienes un WAF o SG que restringe `:80`, ábrelo (la configuración de Traefik redirige a HTTPS, pero Boulder sigue la redirección y acepta la respuesta).
* **`dnsNames` no sustituido.** Si `kubectl get certificate ingest-tls -n agenteye -o jsonpath='{.spec.dnsNames}'` muestra `INGEST_DOMAIN_PLACEHOLDER`, omitiste el paso de `domain.env`; créalo a partir de `domain.env.example` y vuelve a aplicarlo.
* **Limitado por la tasa de Let's Encrypt.** Los pedidos fallidos repetidos para el mismo hostname activan los límites de certificados duplicados o validaciones fallidas. Espera al menos una hora antes de reintentar; revisa el estado del Order para ver el mensaje exacto del límite de tasa.

### El certificado `dashboard-tls` está atascado en `Ready: False` / el navegador sigue mostrando una advertencia

El mismo flujo de diagnóstico que `ingest-tls` anterior (`kubectl describe certificate dashboard-tls -n agenteye`); las causas de DNS, puerto 80, marcador de posición y límite de tasa aplican todas, más dos específicas del dashboard:

* **`DASHBOARD_DOMAIN` resuelve al LoadBalancer incorrecto.** Debe apuntar al LB de Traefik del *dashboard*, no al de ingesta pública. Haz `dig +short` del hostname y compáralo con la dirección del LB del dashboard.
* **La instancia de Traefik del dashboard no puede servir el challenge.** Debe instalarse con el archivo de valores del dashboard incluido, que habilita un proveedor Ingress con alcance limitado para el solucionador HTTP-01 de cert-manager. Sin él, el solucionador no tiene ruta y el Order permanece `pending` indefinidamente. Actualiza la instancia con los valores proporcionados; el challenge pendiente se completará solo a continuación.
* **El LoadBalancer tenía restricciones de IP.** Los rangos de origen aplican también al puerto 80, lo que bloquea los validadores de Let's Encrypt — tanto en la emisión inicial como en cada renovación cada \~75 días. Vuelve a abrir el LB, o coordina un solucionador DNS-01 con soporte antes de restringirlo.

Mientras la emisión falla, el dashboard sigue sirviendo su certificado anterior (o el predeterminado del ingress en una instalación nueva) — el acceso se degrada con una advertencia del navegador, nunca se interrumpe.

### La CLI sigue omitiendo la verificación TLS después de que el dashboard obtuvo un certificado de confianza

`--insecure` se guarda en `cli.json` al iniciar sesión. Una vez que el dashboard sirve un certificado de confianza pública, vuelve a iniciar sesión con `agenteye --base-url https://<your-dashboard-domain> --secure login`; la verificación se guarda nuevamente activada y la advertencia de inicio desaparece.

***

## Problemas del dashboard

### No se puede deshabilitar ni editar el usuario `ADMIN_EMAIL`

Por diseño. El usuario que coincide con `ADMIN_EMAIL` se marca como protegido en cada arranque del servidor: el dashboard oculta el botón Deshabilitar para esa fila, y la API rechaza `DELETE /users/:id` y `PUT /users/:id` contra él con `403 Forbidden`. Un trigger de base de datos también rechaza las instrucciones `UPDATE` directas que deshabilitarían la fila protegida.

Para rotar el administrador de arranque, cambia `ADMIN_EMAIL` en tu entorno y reinicia el servidor. El nuevo correo electrónico se hace upsert como protegido. El administrador anterior conserva el indicador de protección hasta que se elimine en la base de datos (generalmente está bien, ya que el correo anterior sigue siendo un administrador válido hasta que lo elimines explícitamente).

### El dashboard no muestra eventos

1. Confirma que la URL del servidor y la clave API son correctas en las variables de entorno del dashboard (`AGENTEYE_SERVER_URL`, `AGENTEYE_API_KEY`).
2. La clave API del dashboard necesita permiso `events:read`.
3. Confirma que los eventos realmente se han ingestado: `curl http://your-server:8080/events -H "Authorization: Bearer $ADMIN_KEY"`

### `/errors` está vacío pero `/events` muestra filas rojas

Las versiones más recientes del SDK emiten fallos como eventos `agent_end` / `tool_result` / `hook_completed` con `outcome: "error"` en el payload, en lugar de como una fila dedicada `event_type: "error"`. La página `/errors` ahora coincide con ambos: cualquier fila que el stream de `/events` pinta en rojo (explícito `event_type='error'`, payload `outcome`/`status` en el conjunto de fallos, `is_error: true`, o un campo `error` con valor verdadero) aparece en `/errors`. Si anteriormente veías "no hay errores en esta ventana" mientras había filas rojas en `/events`, actualiza el dashboard + servidor juntos (el filtro ampliado es `errored=true` en `GET /events`) y las dos vistas coincidirán.

### `/models`, `/tools` o `/hooks` es lento o no carga en rangos de tiempo amplios

**Síntoma:** en una tabla de eventos grande (millones de filas), abrir `/models`, `/tools` o `/hooks` — o ampliar el rango de tiempo a `7d`, `30d` o `all` — hace que los gráficos giren y luego muestren un error de carga. El servidor registra un `MEMORY_LIMIT_EXCEEDED` de ClickHouse (Código 241) o un timeout de consulta para la solicitud `latency_aggregate`.

**Causa:** las versiones anteriores calculaban los agregados de latencia y distribución de estas páginas con una consulta que leía el `payload` JSON completo del evento sin procesar y emparejaba eventos de solicitud/respuesta con un ordenamiento y unión en memoria. La memoria máxima de la consulta crecía por tanto con el tamaño de la ventana, así que en un tenant ocupado un rango amplio podía superar el límite de memoria por consulta de ClickHouse.

**Solución:** actualiza a una versión que incluya esta corrección. El agregado ahora solo lee las columnas compactas promovidas y empareja eventos con una agregación en streaming, por lo que la memoria máxima ya no escala con el payload sin procesar — las ventanas amplias se mantienen dentro del límite de memoria y responden en una fracción del tiempo. La mejora es completamente del lado de la consulta: se aplica a todos los datos existentes en la siguiente carga de página, sin necesidad de re-ingestión ni relleno.

### El dashboard no carga / página en blanco

Revisa los logs del contenedor del dashboard:

```bash theme={null}
docker logs agenteye-dashboard
```

La causa más frecuente es que `AGENTEYE_SERVER_URL` o `AGENTEYE_API_KEY` faltan o apuntan a un servidor inaccesible.

### Análisis / telemetría del dashboard

El dashboard envía análisis de uso del producto anónimos a PostHog por defecto, enrutados a través de la propia ruta `/ingest` del dashboard (un proxy inverso a `https://us.i.posthog.com`). Enviarlos de forma directa significa que los bloqueadores de anuncios del navegador no los descartan. Esto es independiente de la funcionalidad principal del dashboard:

* Es el **contenedor del dashboard** (no el navegador) quien llega a PostHog. Si su acceso saliente a `https://us.i.posthog.com` está bloqueado, la telemetría falla silenciosamente; el dashboard funciona con normalidad y no se muestran errores a los usuarios.
* No se incluyen datos de agentes, sesiones ni eventos; solo el uso de la interfaz del dashboard.
* Para deshabilitar la telemetría por completo, configura `AE_ANALYTICS_DISABLED=1` en el contenedor del dashboard y reinicia. Ver [Telemetry & privacy](/es/agenteye/deployment#telemetry--privacy) en la guía de despliegue.

### Análisis / telemetría de la CLI

La CLI `agenteye` envía análisis de uso anónimos a PostHog por defecto: qué comandos se ejecutan, estado de éxito/salida y duración. Esto es independiente de la funcionalidad de la CLI:

* La **máquina que ejecuta la CLI** llega directamente a `https://us.i.posthog.com`. Si su acceso saliente está bloqueado, la telemetría falla silenciosamente (el envío tiene un límite de tiempo, por lo que nunca retrasa un comando) y la CLI funciona con normalidad.
* No se incluyen datos de agentes, sesiones ni eventos: los **argumentos y valores de flags** del comando (URL del dashboard, token, correo electrónico, IDs de sesión, filtros de consulta) nunca se envían.
* Para deshabilitarla, configura `AGENTEYE_ANALYTICS_DISABLED=1` (o el inter-herramientas `DO_NOT_TRACK=1`) en el entorno de la CLI. Ver [Telemetry & privacy](/es/agenteye/cli#telemetry--privacy) en la guía de la CLI.

***

## Problemas del asistente de IA

Ver [enterprise-docs/assistant.md](/es/agenteye/assistant) para la configuración completa.

### La burbuja del asistente no aparece

La burbuja está oculta a menos que **todo** lo siguiente sea verdad:

* El usuario conectado tiene el permiso `agent:use`.
* `AGENTEYE_AGENT_URL` está configurado en el dashboard y el servicio `agent` es accesible.
* Hay un endpoint de LLM configurado en el servicio `agent` (`ANTHROPIC_API_KEY`, un gateway via `ANTHROPIC_BASE_URL`, o Bedrock/Vertex). Sin ninguno configurado, el agente reporta "not configured" y la burbuja permanece oculta.

Verifica el estado del agente desde el host del dashboard: `curl http://agent:9100/health` debería devolver `{"status":"ok","llm_configured":true,...}`.

### El asistente dice que no puede leer algo

Las herramientas están restringidas por usuario. Si un usuario no tiene `evaluations:read` (o `events:read`, `dashboards:read`), las herramientas correspondientes no se ofrecen y el asistente dirá que no puede leer esos datos. Otorga el permiso de lectura correspondiente.

### "assistant not configured" (HTTP 503) al enviar

El contenedor `agent` no tiene ningún endpoint de LLM configurado, o el `AGENTEYE_AGENT_TOKEN` del dashboard no coincide con el del agente. Configura ambos y reinicia.

### El contenedor `agent` se reinicia / se queda sin memoria bajo carga

Cada conversación genera un proceso hijo de corta duración. Asegúrate de que el contenedor se ejecute con un proceso init (la imagen usa `tini`; en Compose configura `init: true`) y dale límites de memoria adecuados. Reduce `AGENTEYE_AGENT_MAX_STEPS` si es necesario.

***

## Problemas de la CLI

### `agenteye` falla al iniciar con `ModuleNotFoundError: No module named 'click'`

Una instalación nueva de la CLI `agenteye` en la versión **0.1.6** puede fallar al arrancar con:

```
ModuleNotFoundError: No module named 'click'
```

La versión 0.1.6 dependía de que `click` estuviera instalado indirectamente por `typer`; las versiones actuales de `typer` ya no lo incluyen, por lo que un entorno limpio termina sin el paquete. **Actualiza a la versión 0.1.7 o superior**, que depende de `click` directamente:

```bash theme={null}
pipx upgrade agenteye      # si se instaló con pipx (o: pipx install --force agenteye)
uv tool upgrade agenteye   # si se instaló con uv
pip install --upgrade agenteye
```

Ver [enterprise-docs/cli.md](/es/agenteye/cli) para orientación sobre la instalación.

***

## Problemas del SDK de Python

### No aparecen archivos en `$AGENTEYE_HOME/events/`

El SDK almacena eventos en buffer y los vacía cada 500 ms por defecto. Si tu proceso termina antes del flush, los eventos pueden perderse. Llama a `agenteye.configure(flush_interval=0.1)` para un vaciado más rápido en scripts de corta duración, o asegúrate de que tu proceso se ejecute el tiempo suficiente para un ciclo de flush.

Si `AGENTEYE_HOME` está configurado, verifica que el SDK esté escribiendo en `$AGENTEYE_HOME/events/` y no en `~/.agenteye/events/` (requiere SDK ≥ 0.0.1b5).

### `ValueError: Reserved field names cannot be used as custom fields`

Los nombres `timestamp`, `type` y `environment` están reservados y no pueden usarse como campos personalizados. Pasarlos levanta:

```
ValueError: Reserved field names cannot be used as custom fields: [...]
```

Renombra el campo personalizado que causa el conflicto. Ten en cuenta que `session_id` y `agent_id` son parámetros explícitos de la llamada al evento, no campos personalizados; pasar cualquiera de ellos nuevamente como campo personalizado levanta `TypeError`.

***

## Problemas de monitoreo de salud

### No llegan alertas a Slack (Robusta)

Las alertas de salud de Robusta son **opcionales**; no envía nada hasta que se instala y se apunta a un canal de Slack. Verifica el release y su sink:

```bash theme={null}
kubectl get pods -n robusta          # robusta-runner + robusta-forwarder deben estar Running
kubectl logs -n robusta -l app=robusta-runner --tail=50
```

Causas frecuentes: el `api_key` / `slack_channel` de Slack no fueron configurados (o el token fue revocado); el `api_key` es un token de relay en la nube de Robusta (`robusta integrations slack`) pero el `disableCloudRouting: true` incluido necesita un **bot token** de Slack autohospedado (`xoxb-…`), o configura `disableCloudRouting: false`; el `scope` del sink excluye el namespace donde se ejecutan tus pods (los valores incluidos tienen alcance a `agenteye`); o aún no ha ocurrido ningún fallo. Fuerza una alerta de prueba dejando caer un pod:

```bash theme={null}
kubectl -n agenteye delete pod -l app=clickhouse   # se recreará
```

Ver [enterprise-docs/health-monitoring.md](/es/agenteye/health-monitoring#2-pod-failure-alerting-with-robusta-opt-in) para instalación y configuración.

### El servidor sigue alternando entre `NotReady`

La sonda de preparación llega a `/ready`, que falla cuando Postgres o ClickHouse no son accesibles. Si el servidor alterna entre `NotReady`, una dependencia no está disponible de forma intermitente; revisa los pods de ClickHouse y Postgres y los valores de `CLICKHOUSE_URL` / `DATABASE_URL` del servidor. Confirma lo que reporta `/ready`:

```bash theme={null}
kubectl -n agenteye exec deploy/server -- sh -c 'curl -s localhost:8080/ready'
```

Esta sonda es deliberadamente tolerante (un umbral de fallo generoso), por lo que una alternancia sostenida indica un problema real de dependencia y no una sonda demasiado agresiva. La sonda de actividad permanece en `/health`, por lo que la alternancia de preparación **no** reiniciará el pod.

## Problemas de monitoreo de certificados

### El CronJob no envía notificaciones de Slack

El CronJob `cert-renewal-check` requiere una URL de webhook de Slack almacenada en un Secret. Verifica que exista:

```bash theme={null}
kubectl get secret cert-renewal-notify-config -n agenteye
```

Si no existe, créalo:

```bash theme={null}
kubectl create secret generic cert-renewal-notify-config \
  --namespace agenteye \
  --from-literal=SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
```

Sin el secret, el CronJob sigue ejecutándose y registra los resultados en stdout. Revisa los logs con:

```bash theme={null}
kubectl logs -n agenteye -l job-name --tail=50
```

### El certificado de cliente expiró antes de recibir una notificación

El CronJob se ejecuta cada 12 horas. Si no ha estado en ejecución, verifica su estado:

```bash theme={null}
kubectl get cronjob cert-renewal-check -n agenteye
```

Desencadena una verificación manual:

```bash theme={null}
kubectl create job --from=cronjob/cert-renewal-check manual-cert-check -n agenteye
kubectl logs -n agenteye -l job-name=manual-cert-check
```

Para reemitir el certificado expirado inmediatamente:

```bash theme={null}
cd base/certificates/client-certs
./issue-client-cert.sh <cluster-name>
```

Luego aplica el `collector-mtls-secret.yaml` regenerado en el/los clúster(es) que ejecutan tus colectores y reinícialos:

```bash theme={null}
kubectl apply -f collector-mtls-secret.yaml -n <collector-namespace>
```

***

## Problemas de copias de seguridad

### `agenteye-backup` falla con "No space left on device"

El CronJob `agenteye-backup` vuelca Postgres + ClickHouse en un volumen de trabajo `emptyDir` llamado `backup-tmp` (por defecto `30Gi`), luego **transmite** el archivo `tar` directamente a S3 — el archivo comprimido nunca se escribe de vuelta al espacio de trabajo, por lo que el espacio de trabajo solo necesita contener los *volcados sin procesar*, no los volcados más una segunda copia en disco del archivo. Un pod expulsado / `No space left on device` por tanto significa que los **volcados sin procesar** superan el tamaño del espacio de trabajo (el volcado de `events` de ClickHouse domina y crece con el tiempo). Revisa los logs del job fallido:

```bash theme={null}
kubectl logs -n agenteye -l job-name=<failed agenteye-backup job>
```

Solución: en tu overlay, aumenta el `sizeLimit` del `emptyDir` `backup-tmp` del CronJob por encima del total de tus volcados sin procesar, y asegúrate de que el almacenamiento efímero del nodo pueda realmente contenerlo (`sizeLimit` es un límite, no una reserva). Si los volcados superan el disco de un solo nodo, reemplaza el `emptyDir` con un PVC (EBS/PD) para `backup-tmp`, o comprime los volcados en el origen.

> Las versiones anteriores escribían el `.tar.gz` en el *mismo* espacio de trabajo `20Gi` que los volcados, por lo que `volcados + archivo` lo desbordaban y el pod se expulsaba **antes** de que la subida se ejecutara — lo que parece un fallo de S3 pero en realidad es de disco. Transmitir la subida elimina ese doble uso.

### `agenteye-backup` falla al instalar `curl`

El job se ejecuta en la imagen `postgres:16` e instala `curl` al arrancar para el volcado HTTP de ClickHouse. En un clúster sin acceso de salida a los espejos de paquetes de Debian, el paso `apt-get` falla. Permite ese acceso de salida desde el pod de backup, o incluye `curl` en una imagen de backup personalizada/espejada y referencíala en tu overlay.

### `agenteye-backup` se ejecuta pero nada llega al almacenamiento de objetos

La base incluye un `BACKUP_BUCKET` real (`ts-prod-agenteye/backups`) y el ServiceAccount `agenteye-backup`. El job **transmite** el archivo a S3 (`tar cz … | aws s3 cp - s3://…`). Si el pod de backup no tiene acceso de escritura al bucket, la subida falla — y como el script se ejecuta bajo `set -euo pipefail`, un fallo en cualquier lugar de esa tubería **falla** todo el job en el paso `upload` en lugar de no-op silencioso (el trap EXIT del pod registra `backup FAILED during step: upload`). Este también es el paso al que llegas *después* de corregir una expulsión por espacio de trabajo, por lo que si las copias de seguridad fueron expulsadas anteriormente en el paso de archivo, verifica que la subida ahora funcione. Filtra los logs del job fallido en busca del error de acceso a S3:

```bash theme={null}
kubectl logs -n agenteye -l job-name=<failed agenteye-backup job> | grep -iE 's3|upload|denied'
```

Solución: en tu overlay configura `BACKUP_BUCKET` con un bucket de tu propiedad y anota el ServiceAccount `agenteye-backup` existente con acceso de escritura (IRSA / Workload Identity / Pod Identity). Ver la sección **Backups** de [enterprise-docs/kubernetes-deployment.md](/es/agenteye/kubernetes-deployment).

***

## Evaluaciones / sesiones / consultas respaldadas por ClickHouse

### La barra lateral de la página `/queries` está vacía después de actualizar

Se esperan tres tablas (`events`, `evaluations`, `agent_sessions`). Si la barra lateral del SchemaBrowser está vacía después de la actualización, el servidor no pudo aplicar el DDL de ClickHouse al arrancar. Revisa los logs del servidor en busca de `failed to apply CH DDL statement`:

```bash theme={null}
kubectl logs -n agenteye deploy/server | grep -E 'clickhouse|CH DDL'
```

La causa más frecuente es que ClickHouse no era accesible mientras se ejecutaban las migraciones. El servidor se niega a arrancar si no puede llegar a CH, por lo que un pod atascado generalmente tiene `CrashLoopBackOff` en lugar de una página de consultas rota silenciosamente, pero una aplicación parcial de DDL (una instrucción OK, las siguientes con 5xx) deja el esquema a medias. Reinicia el pod del servidor después de verificar que CH es accesible:

```bash theme={null}
kubectl rollout restart deploy/server -n agenteye
```

### Las nuevas evaluaciones no aparecen en `/sessions` o `/queries`

Después de la actualización, las nuevas evaluaciones se escriben en ClickHouse, no en Postgres, y aparecen en `/sessions` (restringido a `evaluations:read`) y en `/queries`. Si no aparecen:

1. Confirma que el pipeline del evaluador está habilitado (`EVALUATOR_ENDPOINT` configurado en el servidor) y produciendo resultados finales; busca líneas de log `evaluation_finalized`.
2. Confirma que CH es accesible desde el servidor: `kubectl exec -n agenteye deploy/server -- curl -fsS http://clickhouse:8123/ping`.
3. Verifica puntualmente la tabla CH: `kubectl exec -n agenteye clickhouse-0 -- clickhouse-client -q 'SELECT count() FROM agenteye.evaluations'`.

### Las consultas fallan bajo carga con "Memory limit exceeded", o ClickHouse recibe `OOMKilled`

**Síntoma:** bajo carga pesada del dashboard/consultas, las páginas analíticas (el stream de eventos, `/sessions`, la vista de modelos/latencia, el editor SQL) empiezan a fallar o a agotar el tiempo de espera; el servidor alterna brevemente entre `NotReady`; y el pod de ClickHouse muestra un conteo de reinicios creciente. Esto es casi siempre un problema de **memoria**, no de CPU ni de disco.

**Confirma que es memoria** (no un problema de rendimiento que la replicación resolvería):

1. Verifica el pod en busca de muertes por falta de memoria:
   ```bash theme={null}
   kubectl -n agenteye describe pod clickhouse-0 | grep -iE 'Restart Count|Last State|Reason|OOMKilled'
   ```
   `Reason: OOMKilled` / `Exit Code: 137` con un conteo de reinicios creciente es la señal.

2. Pregunta a ClickHouse qué está rechazando:
   ```bash theme={null}
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT name, value FROM system.errors WHERE value>0 ORDER BY value DESC"
   ```
   Un conteo grande de `MEMORY_LIMIT_EXCEEDED` es la firma. El mensaje dice *"maximum: N GiB"* — ese **N es `0.9 × el límite de memoria del pod`** (el `max_server_memory_usage_to_ram_ratio` en `deploy/base/clickhouse/configmap.yaml`). Si tus lecturas pesadas necesitan más que N, son rechazadas.

3. Descarta las cosas que *no* son el problema — si la CPU, el conteo de partes y el disco son todos bajos, añadir réplicas/sharding sería un coste desperdiciado:
   ```bash theme={null}
   kubectl -n agenteye top pod clickhouse-0
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT table, count() parts FROM system.parts WHERE active AND database='agenteye' GROUP BY table"
   ```

**Causa:** el límite de memoria del pod de ClickHouse es demasiado pequeño para el conjunto de trabajo analítico. Las lecturas más pesadas extraen la columna `payload` JSON sin procesar, ejecutan `JSONExtract*` sobre ella y usan `FINAL` — cada una puede necesitar varios GiB. Si las cachés configuradas (`mark_cache_size` + `uncompressed_cache_size`) son más grandes que el pod, se agravan: las cachés se cobran contra el mismo presupuesto y eliminan la memoria de las consultas.

**Solución — escala la memoria de ClickHouse:**

1. Aumenta el límite de memoria de ClickHouse en tu overlay parcheando los `resources` del contenedor del StatefulSet `clickhouse` (el mismo mecanismo de overlay usado para los `resources` de los demás componentes). El presupuesto del servidor utilizable es `0.9 × límite`, por lo que un límite de `6Gi` da \~5.4 GiB, `16Gi` da \~14 GiB. Configura también `requests.memory` con un valor base real, para que el planificador lo reserve. Aplicar esto **recrea el pod de CH** (réplica única → \~30–60s de tiempo de inactividad en análisis); hazlo en una ventana de bajo tráfico.
2. Mantén las cachés en `deploy/base/clickhouse/configmap.yaml` proporcionales al límite — las cachés pequeñas (unos pocos cientos de MiB) son seguras en un pod pequeño; solo auméntalas junto con un aumento correspondiente del límite de memoria. El `max_memory_usage` por consulta se configura explícitamente en el perfil `users.xml` (ver la sección de nodo fijo a continuación) y se mantiene por debajo del límite a nivel de servidor (`0.9 × límite`) para que ninguna consulta individual pueda usar *más* RAM que la que tiene el contenedor.
3. Si el nodo en sí es el techo, verifica la memoria del host que ClickHouse puede ver:
   ```bash theme={null}
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT formatReadableSize(value) FROM system.asynchronous_metrics WHERE metric='OSMemoryTotal'"
   ```
   Si eso es solo un poco por encima del límite del pod, mueve ClickHouse a un nodo más grande (optimizado para memoria) — a través de un selector/afinidad de nodo en tu overlay — antes de aumentar más el límite.

**Cuando no puedes añadir memoria: ejecuta las consultas en RAM y falla rápido — no hagas derrame en un disco lento.** Si el nodo es fijo y el pod no puede crecer, limita lo que cada consulta individual puede usar (para que una consulta no acapare todo el nodo) y, en un **disco de datos lento (no SSD)**, **no** permitas que grandes agregaciones/ordenamientos se derramen al disco. Derramarse a un disco lento es más lento que el timeout de lectura del cliente del servidor, por lo que una consulta con derrame devuelve un `500` del dashboard a mitad de ejecución mientras ClickHouse sigue procesando — mantener las consultas en RAM y rechazar la rara que supera el presupuesto *rápido* (`MEMORY_LIMIT_EXCEEDED`, en menos de un segundo) es lo que restaura la carga. Ten en cuenta un detalle importante de ClickHouse para aplicar esto:

* **Estas son configuraciones de *perfil*, y ClickHouse solo lee `<profiles>` desde `users_config` (`users.xml` / `users.d/*.xml`) — nunca desde `config.d`.** Un bloque `<profiles>` colocado en `config.d/agenteye.xml` es **ignorado silenciosamente** (`max_execution_time`, `max_memory_usage`, etc. simplemente no se aplican). La configuración incluida por tanto las envía como una clave `users.xml` en el ConfigMap `clickhouse-config`, montada en `/etc/clickhouse-server/users.d/agenteye.xml`.
* Los valores por defecto incluidos: `max_memory_usage` (límite por consulta — una consulta no puede consumir todo el presupuesto del servidor), `max_bytes_before_external_group_by` / `max_bytes_before_external_sort` = **`0` (derrame deshabilitado)** para que las consultas permanezcan en RAM en lugar de arrastrarse en el disco lento, y `max_execution_time` (guardia contra consultas desbocadas, alineado con el timeout de lectura del cliente del servidor).
* **Verifica que están activas** (así también detectas el error de config.d):
  ```bash theme={null}
  kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
    "SELECT name, value FROM system.settings
     WHERE name IN ('max_memory_usage','max_bytes_before_external_group_by','max_execution_time')"
  ```
  Espera un `max_memory_usage` distinto de cero y `max_bytes_before_external_group_by = 0`. Si `max_memory_usage` muestra `0`/predeterminado, el perfil no se está aplicando — verifica que la configuración esté en un montaje `users.d`, no en `config.d`.

Concesión: con el derrame deshabilitado, una consulta cuyo conjunto de trabajo supera `max_memory_usage` es **rechazada** (`MEMORY_LIMIT_EXCEEDED`) en lugar de completarse lentamente — en un disco lento ese rechazo rápido es preferible, porque una consulta con derrame superaría de todas formas el timeout del cliente y fallaría. Si tu disco de datos es **rápido (SSD)**, puedes en cambio aumentar los umbrales `max_bytes_before_external_*` para permitir que las consultas grandes se derramen al disco y se completen.

***

## Multi-tenencia (organizaciones)

### Errores durante la actualización que habilita organizaciones (pods de servidor mixtos viejo/nuevo)

**Síntoma:** durante un despliegue progresivo de la versión que habilita organizaciones, algunas solicitudes fallan: los logs del servidor muestran `there is no unique or exclusion constraint matching the ON CONFLICT specification` en la ruta `api_keys`, y/o los canales de alertas/Slack/webhook dejan de dispararse mientras el despliegue está en curso.

**Causa:** la actualización reemplaza el antiguo índice único de toda la instancia en `api_keys(name)` con índices parciales por organización, y mueve la configuración de canales de alerta (y `default_user_permissions`) fuera de la tabla global `settings` hacia `org_settings` por organización. Un pod de servidor **antiguo** aún emite `ON CONFLICT (name)` (ahora sin restricción coincidente) y aún lee la configuración del canal desde las filas antiguas de `settings` (ahora vacías). Los pods viejos y nuevos no pueden coexistir de forma segura para estas dos rutas.

**Solución:** no hagas un despliegue progresivo lento de esta actualización específica entre versiones mixtas. Realiza un cambio limpio: escala el servidor antiguo a cero (o usa una breve ventana de mantenimiento) y levanta la nueva versión junto con sus migraciones, en lugar de ejecutar réplicas antiguas y nuevas en paralelo. El tráfico normal y la ingesta se reanudan inmediatamente después del cambio; esto solo afecta la ventana de transición de versión.

### El provisionamiento de una organización falla en `CREATE USER` / `CREATE ROW POLICY`, o una organización puede leer los datos de otra

**Síntoma:** crear una organización devuelve un error que menciona `CREATE USER`, `CREATE ROW POLICY` o "access management is disabled"; o, peor, los miembros de una organización ven los eventos/evaluaciones de otra organización en el editor SQL o en el asistente.

**Causa:** el aislamiento por organización se aplica mediante un usuario de ClickHouse dedicado + política de filas por organización. Esto requiere que la **gestión de acceso** SQL esté habilitada y que `users_without_row_policies_can_read_rows=false` en ClickHouse. Con la gestión de acceso desactivada, el provisionamiento no puede crear el usuario/política; con el valor predeterminado de la política de filas en su valor permisivo, un usuario que tiene SELECT pero ninguna política lee **todas** las filas (fail-open).

**Solución:** usa la configuración incluida en `deploy/base/clickhouse/`, que configura ambos. Si ejecutas tu propia configuración de ClickHouse, habilita la gestión de acceso SQL en el usuario interno del servidor y configura `users_without_row_policies_can_read_rows=false` (ver `deploy/base/clickhouse/configmap.yaml`), luego reinicia ClickHouse y vuelve a crear la organización con la CLI `agenteye-orgctl` (ver [enterprise-docs/tenant-management.md](/es/agenteye/tenant-management)).

### Los usuarios de la organización pierden acceso a ClickHouse después de cambiar `ORG_CH_SECRET`

**Síntoma:** el editor SQL y el asistente de IA de repente devuelven fallos de autenticación de ClickHouse para todas las organizaciones, inmediatamente después de que se cambió `ORG_CH_SECRET` o se configuró de forma inconsistente entre réplicas.

**Causa:** la contraseña de ClickHouse de cada organización se deriva como un HMAC de `ORG_CH_SECRET`. Rotarla (o ejecutar réplicas con valores diferentes) invalida la credencial de ClickHouse almacenada de cada organización; la contraseña derivada ya no coincide con el usuario provisionado.

**Solución:** configura `ORG_CH_SECRET` con un valor único y robusto **antes** de provisionar una segunda organización y mantenlo estable e idéntico en todas las réplicas del servidor. La reconciliación al arrancar el servidor vuelve a provisionar el usuario de ClickHouse de cada organización desde el secreto actual al arrancar, por lo que un reinicio del servidor en todas las réplicas (con el secreto consistente) sana los usuarios huérfanos. Trata el valor como un secreto de larga duración; no lo rotas casualmente. Como red de seguridad, si `ORG_CH_SECRET` se deja en el valor de desarrollo predeterminado incorporado (es decir, sin configurar), la reconciliación al arrancar **omite** las organizaciones no predeterminadas y registra un error en lugar de reescribir sus credenciales de ClickHouse al valor de desarrollo conocido públicamente, para que una réplica que reinicia sin el secreto no pueda romper las demás réplicas. Configura el secreto de forma consistente y reinicia para provisionar esas organizaciones.

### El asistente de IA devuelve 400 / se niega a chatear después de habilitar organizaciones

**Síntoma:** el dock del asistente carga pero cada mensaje vuelve como un error (HTTP `400`), y el agente registra una solicitud `/chat` sin organización rechazada.

**Causa:** el agente es consciente de la organización y falla de forma cerrada; rechaza un `/chat` que no lleva contexto de organización. Esto ocurre durante un despliegue de transición donde el agente se ha actualizado pero el dashboard que envía la solicitud aún no es consciente de la organización.

**Solución:** completa el despliegue para que el dashboard envíe el contexto de la organización (el estado final normal, sin necesidad de ningún flag). Para salvar la brecha mientras un dashboard no consciente de la organización habla con un agente consciente de la organización, configura `AGENTEYE_AGENT_ALLOW_NO_ORG=1` en el servicio `agent` para que recurra a la organización `default` en lugar de rechazar, y elimínalo una vez que la actualización del dashboard aterrice. Ver la referencia de variables de entorno en [enterprise-docs/assistant.md](/es/agenteye/assistant#environment-variable-reference).

***

## Auditorías

### Una auditoría nunca se ejecuta (la próxima ejecución sigue desplazándose, sin historial de ejecución)

**Síntoma:** la página de auditoría muestra *última ejecución: nunca*, o `próxima ejecución` sigue moviéndose al futuro sin que aparezca ninguna fila en el historial de ejecución.

**Causa:** la auditoría está deshabilitada (las auditorías deshabilitadas no tienen entrada en la cola), o los trabajadores de auditoría del servidor están fallando al reclamar trabajo.

**Solución:** confirma que la auditoría está **habilitada** (el botón ejecutar ahora lo requiere). Luego revisa los logs del servidor en busca de `audits pipeline started` al arrancar y de errores `audits:` — una línea `claim_due failed` apunta a conectividad con Postgres. `AUDIT_WORKERS` por defecto es `1`; debe ser ≥ 1 para que se ejecute cualquier auditoría.

### Las ejecuciones de auditoría tienen éxito pero no encuentran nada

**Síntoma:** el historial de ejecución muestra `succeeded` con `findings: 0` aunque `/errors` claramente muestra fallos.

**Causa:** la ventana de análisis no cubre los fallos, o los filtros de alcance los excluyen.

**Solución:** verifica la ventana de la ejecución (`window_from → window_to`) contra cuándo ocurrieron los fallos — en el modo `since_last` cada ejecución solo analiza desde la última ejecución exitosa, por lo que los fallos más antiguos solo los ve la *primera* ejecución o una auditoría con ventana `fixed`. Amplía `scope` (entornos / IDs de agente). Las estadísticas de ejecución muestran `policy_hits` (cuántas políticas deterministas se activaron) e `improvements` (cuántas registró la investigación de IA) — si ambos son 0, la ventana/alcance genuinamente no encontró nada.

### La ejecución dice `analysis_unavailable` y solo produce hallazgos de políticas

**Síntoma:** las estadísticas de ejecución incluyen `analysis_unavailable` y los únicos hallazgos son `kind: policy`; no aparecen mejoras de IA.

**Causa:** la investigación agente no pudo ejecutarse: el servidor no puede llegar al servicio de agente (`AGENTEYE_AGENT_URL` / `AGENTEYE_AGENT_TOKEN` no configurados en el **servidor** — la auditoría reutiliza la conexión del asistente), el servicio de asistente no tiene LLM configurado, o la llamada tuvo error/timeout (la cadena `analysis_unavailable` tiene el detalle). El paso de políticas deterministas es el mínimo — siempre se ejecuta — por lo que la auditoría aún tiene éxito con sus hallazgos de seguridad.

**Solución:** configura `AGENTEYE_AGENT_URL` (p. ej. `http://agent:9100`) y `AGENTEYE_AGENT_TOKEN` en el **servidor** — los mismos valores que ya usa el asistente del dashboard (los manifiestos/compose incluidos ahora los conectan) — y configura un LLM en el servicio de asistente (ver [assistant.md](/es/agenteye/assistant)), luego vuelve a ejecutar. Una investigación grande puede necesitar un `AUDIT_LLM_TIMEOUT_MS` más grande (servidor) — mantenlo por encima del `AGENTEYE_AUDIT_TIMEOUT_MS` del agente.

### El sandbox de código de auditoría está deshabilitado (`sandbox_available: false`)

**Síntoma:** el `/health` del agente muestra `sandbox_available: false`, y las ejecuciones de auditoría indican que el sandbox no está disponible; la IA investiga solo con SQL.

**Causa:** el sandbox bubblewrap en el pod necesita **espacios de nombres de usuario sin privilegios**, que el perfil seccomp del pod o el kernel del nodo está bloqueando.

**Solución:** configura `seccompProfile: Unconfined` (k8s) o `security_opt: [seccomp:unconfined]` (compose) en el agente, y confirma que el kernel del nodo permite espacios de nombres de usuario sin privilegios (algunas imágenes gestionadas, p. ej. GKE COS, los deshabilitan). Donde no puedas habilitarlo, esto es lo esperado y seguro — el auditor degrada automáticamente a solo SQL. Ver [deployment.md](/es/agenteye/deployment).

### El informe de auditoría por correo electrónico no se entrega

**Síntoma:** una auditoría encontró nuevos hallazgos pero no llegó ningún correo electrónico.

**Causa:** la auditoría no tiene ningún canal de **correo electrónico** adjunto, el correo electrónico está deshabilitado a nivel de organización en `alerts.enabled_channels`, no hay destinatarios, o SMTP no está configurado.

**Solución:** adjunta un canal de correo electrónico a la auditoría, asegúrate de que `email` esté en `alerts.enabled_channels`, configura destinatarios (en el canal o mediante `alerts.email_default_recipients`), y configura SMTP (el mismo transporte que usan los correos de alertas + OTP). El correo solo se envía cuando una ejecución produce **al menos un** hallazgo nuevo.

### Un patrón silenciado o descartado conserva su página de hallazgo antigua pero nunca se reclasifica

**Síntoma:** después de silenciar un hallazgo, las ejecuciones posteriores nunca vuelven a mostrar ese patrón — aunque siga ocurriendo.

**Causa:** ese es el comportamiento diseñado: silenciar/descartar son supresiones duraderas vinculadas a la huella digital del patrón.

**Solución:** abre el hallazgo y usa **reopen** para borrar la supresión; la próxima ejecución clasificará el patrón de nuevo. Usa **resolve** (no silenciar) para los patrones "corregidos" sobre los que querrías enterarte si regresan.

***

## Obtener ayuda

Contacta a `support@exosphere.host` con:

* Tu versión de AgentEye (del tag de la versión)
* Los logs relevantes del contenedor (`docker logs <container>`)
* Una descripción del problema y lo que ya has intentado
