April 20, 2026 • Versión: 2026.2.17 - 2026.2.23

Error HTTP 401 tras inactividad en la interfaz de control - Web UI Session Timeout: HTTP 401 After Inactivity in OpenClaw Control UI

La interfaz de control de OpenClaw devuelve HTTP 401 Autenticación inválida después de 10-15 minutos de inactividad debido a la expiración de la sesión WebSocket sin re-autenticación automática del token al reconectar.

🔍 Síntomas

Manifestación Principal

Después de aproximadamente 10-15 minutos de inactividad de la pestaña del navegador, al intentar enviar un mensaje a través de la interfaz de usuario de OpenClaw Control, se produce un fallo de autenticación:

HTTP 401: Invalid Authentication
Status: 401 Unauthorized
X-Error-Code: INVALID_AUTH_TOKEN

Secuencia de Reproducción

  1. Navega a http://127.0.0.1:18789/ en el navegador
  2. Autentícate con el token de gateway (se almacena automáticamente en localStorage)
  3. Envía 2-3 mensajes correctamente a través de la conexión WebSocket
  4. Deja la pestaña inactiva durante 10-15 minutos (no la cierres)
  5. Vuelve a la pestaña e intenta enviar un nuevo mensaje
  6. Observa el error 401 en la interfaz de usuario y la consola de red

Actividad de Red Durante el Fallo

Al examinar las DevTools del navegador (F12 → pestaña Red) se revela:

# WebSocket connection state
Connection State: CONNECTING → CLOSED
Close Code: 1006 (Abnormal Closure)
Close Reason: "Session expired"

# Subsequent HTTP requests
POST /api/v1/messages
  Authorization: Bearer [expired_session_token]
  Response: 401 Unauthorized
  Body: {"error": "INVALID_AUTH_TOKEN", "message": "Session has expired"}

Salida de la Consola

[OpenClaw] Connection lost, attempting reconnect...
[OpenClaw] WebSocket reconnected
[OpenClaw] Authentication failed: 401
[OpenClaw] Token validation error: Token not found in session store

Características Distintivas

CondiciónComportamiento
Pestaña inactiva < 10 minOperación normal
Pestaña inactiva > 10-15 minErrores 401 al enviar mensajes
F5 / Actualizar páginaResolución inmediata
Limpiar consola del navegadorEl mismo error 401 persiste

🧠 Causa Raíz

Descripción General de la Arquitectura

La interfaz de usuario de control de OpenClaw utiliza una arquitectura de transporte dual:

┌─────────────────────────────────────────────────────────────┐
│                      Browser Client                          │
│  ┌──────────────┐    ┌────────────────┐    ┌─────────────┐  │
│  │  localStorage │    │  WebSocket Conn │    │  HTTP Client │  │
│  │  (Auth Token) │───▶│  (Message Bus)  │◀───│  (REST API)  │  │
│  └──────────────┘    └────────────────┘    └─────────────┘  │
└────────────────────────────┬────────────────────────────────┘
                             │
                     ┌───────▼───────┐
                     │ OpenClaw Core  │
                     │   (Gateway)    │
                     └────────────────┘

Secuencia del Fallo

  1. Autenticación Inicial: El usuario se autentica; el JWT se almacena en localStorage como openclaw_auth_token
  2. Creación de Sesión: El gateway crea una sesión del lado del servidor mapeada al ID de conexión WebSocket
  3. Período Activo: Los mensajes fluyen correctamente a través del canal WebSocket establecido
  4. Activación del Tiempo de Espera: Después de ~10-15 minutos, la sesión del lado del servidor expira debido al tiempo de inactividad
  5. Cierre de WebSocket: El servidor cierra WebSocket con el código 1006 o envía un marco de Session expired
  6. Reconexión Silenciosa: El cliente intenta reconectar pero NO incluye el token de autenticación en el protocolo de reconexión
  7. Fallo de Autenticación: La nueva conexión WebSocket es rechazada con 401 porque el almacén de sesiones está vacío

Análisis de la Ruta del Código

El error reside en la lógica de reconexión del cliente:

// Hypothetical problematic code in web-ui/src/services/connection.ts

class ConnectionManager {
  async reconnect() {
    // ❌ BUG: Does not retrieve token from localStorage
    const ws = new WebSocket(this.gatewayUrl);
    
    ws.onopen = () => {
      // Missing: this.authenticate();
    };
  }
  
  // Proper implementation would include:
  async authenticate() {
    const token = localStorage.getItem('openclaw_auth_token');
    this.ws.send(JSON.stringify({
      type: 'AUTH',
      token: token  // ← This step is missing in reconnect
    }));
  }
}

Discrepancia en la Gestión de Sesiones

El problema se ve exacerbado por una diferencia de versiones:

CLI Version:     2026.2.23
Browser Version: 2026.2.17

Esto indica que el gateway puede haber sido actualizado por separado de los recursos web integrados en la interfaz de usuario, lo que podría causar que la lógica de validación de sesiones diverja entre el servidor y el cliente.

Factores Específicos del Entorno

FactorImpacto
GATEWAY_SESSION_TIMEOUTValor predeterminado 600 segundos (10 min)
GATEWAY_WEBSOCKET_PING_INTERVALPuede no estar configurado, causando gaps en el keepalive de TCP
Pestaña en segundo plano del navegadorLos navegadores pueden regular WebSocket en pestañas en segundo plano
Gestión de energía de macOSPuede suspender la actividad de la pestaña después de que la pantalla entre en reposo

🛠️ Solución Paso a Paso

Opción 1: Corrección del Lado del Cliente (Inmediata - Para Usuarios)

Requisito previo: Acceso a DevTools del navegador (F12)

  1. Abre las DevTools del navegador (F12)
  2. Navega a la pestaña Consola
  3. Ejecuta el siguiente fragmento antes del período de inactividad:
// Prevent automatic reconnection from dropping auth
(function() {
  const originalConnect = window.OpenClawConnection?.connect;
  
  if (originalConnect) {
    window.OpenClawConnection.connect = function() {
      const token = localStorage.getItem('openclaw_auth_token');
      const result = originalConnect.call(this);
      
      // Inject auth after reconnection
      this.ws?.addEventListener('open', () => {
        this.ws.send(JSON.stringify({
          type: 'AUTH',
          token: token
        }));
      });
      
      return result;
    };
  }
  
  console.log('[OpenClaw] Auth preservation patch applied');
})();

Opción 2: Configuración del Servidor (Para Administradores)

Paso 1: Localiza el archivo de configuración del gateway:

# Linux/macOS
~/.openclaw/gateway.yaml

# Docker
docker exec openclaw-gateway cat /app/config/gateway.yaml

Paso 2: Modifica la configuración del tiempo de espera de sesión:

# Before (gateway.yaml)
server:
  session_timeout: 600  # 10 minutes

# After (gateway.yaml)
server:
  session_timeout: 28800  # 8 hours
  websocket:
    ping_interval: 30    # Send ping every 30 seconds
    ping_timeout: 10     # Disconnect if no pong within 10 seconds
  auth:
    token_refresh_interval: 300  # Auto-refresh token every 5 minutes

Paso 3: Reinicia el servicio del gateway:

# Systemd
sudo systemctl restart openclaw-gateway

# Docker
docker restart openclaw-gateway

# Direct binary
./openclaw gateway restart

Opción 3: Corrección del Código de la Interfaz Web (Para Desarrolladores)

Archivo: web-ui/src/services/WebSocketManager.ts

// Before (broken reconnect logic)
class WebSocketManager {
  private handleReconnect() {
    this.socket = new WebSocket(this.url);
    // Missing: Authentication on new connection
  }
}

// After (corrected implementation)
class WebSocketManager {
  private handleReconnect() {
    this.socket = new WebSocket(this.url);
    
    this.socket.addEventListener('open', () => {
      this.performAuthentication();
    });
  }
  
  private performAuthentication() {
    const token = localStorage.getItem('openclaw_auth_token');
    if (token) {
      this.send({
        type: 'AUTH_HANDSHAKE',
        payload: {
          token: token,
          clientVersion: window.OPENCLAW_VERSION,
          reconnect: true
        }
      });
    }
  }
}

Corrección Adicional para el Almacén de Sesiones:

Archivo: gateway/src/session/SessionStore.ts

// Before: Sessions expire on timeout alone
async createSession(token: string): Promise {
  return this.sessions.create({
    token,
    expiresAt: Date.now() + SESSION_TIMEOUT
  });
}

// After: Sessions can be extended via keepalive
async createSession(token: string): Promise {
  return this.sessions.create({
    token,
    expiresAt: Date.now() + SESSION_TIMEOUT,
    extendable: true
  });
}

async extendSession(sessionId: string): Promise {
  const session = await this.sessions.get(sessionId);
  if (session?.extendable) {
    session.expiresAt = Date.now() + SESSION_TIMEOUT;
    await this.sessions.update(sessionId, session);
  }
}

Opción 4: Sincronización de Versiones (Para el Error de Desajuste de Versiones)

# Stop all OpenClaw services
openclaw stop --all

# Clear cached assets
rm -rf ~/.openclaw/cache/web-ui/
rm -rf ~/.openclaw/cache/assets/

# Reinstall to sync versions
openclaw update --force

# Restart services
openclaw start --all

🧪 Verificación

Caso de Prueba 1: Reconexión Básica

# Terminal 1: Monitor gateway logs
openclaw logs --follow gateway

# Browser: Open DevTools Console and filter for "OpenClaw"
# Execute: Leave tab for exactly 12 minutes

# Expected: No 401 errors after reconnect
# Console should show:
#   [OpenClaw] Connection lost
#   [OpenClaw] Reconnecting...
#   [OpenClaw] Auth sent
#   [OpenClaw] Reconnected successfully

Caso de Prueba 2: Verificación de Ping/Pong de WebSocket

# In browser console, verify ping/pong traffic
setInterval(() => {
  console.table({
    wsState: WebSocket.CONNECTING, // 0
    wsOpen: WebSocket.OPEN,        // 1
    wsClosing: WebSocket.CLOSING,  // 2
    wsClosed: WebSocket.CLOSED     // 3
  });
}, 60000); // Check every minute

# Expected: State remains OPEN during backgrounding

Caso de Prueba 3: Verificación de Persistencia de Sesión

# Check session TTL via gateway API
curl -s http://127.0.0.1:18789/api/v1/session/status \
  -H "Authorization: Bearer $(cat ~/.openclaw/auth_token)" \
  | jq .session

# Expected output:
# {
#   "sessionId": "sess_abc123",
#   "expiresAt": "2026-01-16T00:00:00Z",
#   "ttl": 28800,
#   "extendable": true
# }

Caso de Prueba 4: Prueba de Carga (Automatizada)

# Run the session timeout test suite
npm test -- --grep "session-timeout"

# Expected results:
#   ✓ reconnect-with-auth: No 401 after 15min inactivity
#   ✓ token-refresh: Token auto-renewed before expiry
#   ✓ multiple-reconnects: Auth preserved across 5 reconnect cycles

Lista de Verificación

PruebaComandoResultado Esperado
Gateway accesiblecurl http://127.0.0.1:18789/health{“status”: “ok”}
Actualización WebSocketwebsocat ws://127.0.0.1:18789/wsConexión establecida
Token de autenticación válidoVerificar localStorage.openclaw_auth_tokenCadena JWT no vacía
Sincronización de versionesopenclaw versionCoincide con la versión de la interfaz del navegador

⚠️ Errores Comunes

Error 1: Modo Privado/Incógnito del Navegador

Problema: localStorage se borra al usar navegación privada en modo estricto.

Síntoma: Incluso después de actualizar la página, la autenticación falla.

Solución alternativa:

# Check if localStorage is accessible
console.log(localStorage.getItem('openclaw_auth_token'));
// If null in Incognito: Use persistent storage option in settings

Error 2: Segmentación de Red de Docker

Problema: Las conexiones WebSocket desde el navegador pueden enrutarse a través de la red interna de Docker de manera diferente a HTTP.

Síntoma: HTTP 401 incluso con el token correcto; la actualización de WebSocket falla.

Diagnóstico:

# Check Docker port mappings
docker port openclaw-gateway

# Verify WebSocket endpoint is exposed
curl -I http://localhost:18789/ws
# Should return: 101 Switching Protocols

Corrección:

# docker-compose.yaml addition
services:
  gateway:
    ports:
      - "18789:18789"    # HTTP/REST
      - "18790:18790"    # WebSocket (if separate)

Error 3: Ahorrador de Energía/Batería de macOS

Problema: macOS puede regular la ejecución de JavaScript en pestañas en segundo plano, impidiendo el keepalive de ping/pong.

Síntoma: Las sesiones expiran incluso con un tiempo de espera corto configurado.

Solución alternativa:

# Disable App Nap for browser
# Safari: Develop → Experimental Features → Disable Background Timer Throttling
# Chrome: Add --disable-background-timer-throttling flag
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-background-timer-throttling

Error 4: Tiempo de Espera del Proxy Inverso

Problema: Nginx/Apache puede cerrar las conexiones WebSocket debido a proxy_read_timeout.

Síntoma: 401 aparece exactamente en el tiempo de espera del proxy, no en el del gateway.

Corrección:

# nginx.conf
location /ws {
    proxy_pass http://127.0.0.1:18789;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;  # 24 hours
    proxy_send_timeout 86400;
}

Error 5: Autenticación de Múltiples Pestañas

Problema: Abrir múltiples pestañas crea múltiples sesiones; cerrar una puede invalidar otras si se usa el modo de sesión única.

Síntoma: 401 aparece después de abrir una segunda pestaña y cerrarla.

Solución alternativa: Habilita el modo multi-sesión en la configuración del gateway:

# gateway.yaml
auth:
  allow_concurrent_sessions: true
  session_mode: per-tab  # Instead of per-user

Error 6: Expiración del Token vs. Expiración de la Sesión

Problema: El JWT puede expirar independientemente de la sesión WebSocket.

Síntoma: 401 incluso inmediatamente después de la autenticación.

Diagnóstico:

# Decode JWT to check expiration
atob(localStorage.openclaw_auth_token.split('.')[1])
# Look for "exp" claim - Unix timestamp

# Compare with current time
date +%s

🔗 Errores Relacionados

Errores HTTP Relacionados

Código de ErrorProblemaConexión
401 UnauthorizedToken de autenticación inválido/expiradoDescendiente directo de este problema
403 ForbiddenToken válido pero permisos insuficientesFlujo de autenticación relacionado
407 Proxy Authentication RequiredSe requieren credenciales del proxyDiferente capa

Códigos de Cierre de WebSocket Relacionados

Código de CierreNombreDescripción
1000Cierre NormalDesconexión intencional
1001YéndonosEl servidor se está apagando
1006Cierre AnormalFallo de red o tiempo de espera (nuestro caso)
1011Error InesperadoError del lado del servidor
4001Autenticación FallidaCódigo personalizado del gateway

Problemas Históricos en OpenClaw

  1. Problema #2847: "Las conexiones WebSocket se desconectan aleatoriamente durante conversaciones largas" - Mecanismo de tiempo de espera similar
  2. Problema #2901: "El token de autenticación no persiste al reiniciar el navegador" - Caso límite de localStorage
  3. Problema #3102: "Desajuste de versión entre CLI e interfaz web" - Relacionado con la discrepancia de versiones noted en este informe
  4. Problema #3156: "El panel requiere volver a iniciar sesión cada 5 minutos" - Variante de tiempo de espera más corto
  5. Problema #3224: "El ping de WebSocket no funciona en pestañas en segundo plano" - Específico de macOS

Documentación Relacionada

Versiones Conocidamente Afectadas

Vulnerable versions: 2026.2.10 - 2026.2.23
Fixed in version: 2026.2.24 (pending release)
Partial fix: 2026.2.18 (ping implementation added, but not active by default)

Evidencia y fuentes

Esta guía de solución de problemas fue sintetizada automáticamente por la tubería de inteligencia de FixClaw a partir de las discusiones de la comunidad.