[Web-UI-Sitzungszeitüberschreitung: HTTP 401 nach Inaktivität] - Web UI Session Timeout: HTTP 401 After Inactivity in OpenClaw Control UI
Die OpenClaw Control UI gibt nach 10-15 Minuten Inaktivität aufgrund des Ablaufs der WebSocket-Sitzung ohne automatische Token-Re-Authentifizierung beim erneuten Verbinden den HTTP 401 Invalid Authentication-Fehler zurück.
🔍 Symptome
Primäre Manifestation
Nach ungefähr 10-15 Minuten Inaktivität des Browser-Tabs führt ein Versuch, eine Nachricht über die OpenClaw Control UI zu senden, zu einem Authentifizierungsfehler:
HTTP 401: Invalid Authentication
Status: 401 Unauthorized
X-Error-Code: INVALID_AUTH_TOKEN
Reproduktionssequenz
- Navigieren Sie zu
http://127.0.0.1:18789/im Browser - Authentifizieren Sie sich mit dem Gateway-Token (wird automatisch in
localStoragegespeichert) - Senden Sie 2-3 Nachrichten erfolgreich über die WebSocket-Verbindung
- Lassen Sie den Tab 10-15 Minuten inaktiv (schließen Sie ihn nicht)
- Kehren Sie zum Tab zurück und versuchen Sie, eine neue Nachricht zu senden
- Beobachten Sie den
401-Fehler in der UI und der Netzwerkkonsole
Netzwerkaktivität während des Fehlers
Die Untersuchung der Browser DevTools (F12 → Netzwerk-Tab) zeigt:
# WebSocket-Verbindungsstatus
Connection State: CONNECTING → CLOSED
Close Code: 1006 (Abnormal Closure)
Close Reason: "Session expired"
# Nachfolgende HTTP-Anfragen
POST /api/v1/messages
Authorization: Bearer [expired_session_token]
Response: 401 Unauthorized
Body: {"error": "INVALID_AUTH_TOKEN", "message": "Session has expired"}
Konsolenausgabe
[OpenClaw] Connection lost, attempting reconnect...
[OpenClaw] WebSocket reconnected
[OpenClaw] Authentication failed: 401
[OpenClaw] Token validation error: Token not found in session store
Unterscheidungsmerkmale
| Bedingung | Verhalten |
|---|---|
| Tab inaktiv < 10 Min. | Normaler Betrieb |
| Tab inaktiv > 10-15 Min. | 401-Fehler beim Nachrichtensenden |
| F5 / Seitenaktualisierung | Sofortige Lösung |
| Browser-Konsole leeren | Gleicher 401-Fehler bleibt bestehen |
🧠 Ursache
Architektonische Übersicht
Die OpenClaw Control UI verwendet eine Dual-Transport-Architektur:
┌─────────────────────────────────────────────────────────────┐
│ Browser Client │
│ ┌──────────────┐ ┌────────────────┐ ┌─────────────┐ │
│ │ localStorage │ │ WebSocket Conn │ │ HTTP Client │ │
│ │ (Auth Token) │───▶│ (Message Bus) │◀───│ (REST API) │ │
│ └──────────────┘ └────────────────┘ └─────────────┘ │
└────────────────────────────┬────────────────────────────────┘
│
┌───────▼───────┐
│ OpenClaw Core │
│ (Gateway) │
└────────────────┘
Fehlersequenz
- Erstauthentifizierung: Der Benutzer authentifiziert sich; JWT wird als
openclaw_auth_tokeninlocalStoragegespeichert - Sitzungserstellung: Gateway erstellt serverseitige Sitzung, die mit der WebSocket-Verbindungs-ID verknüpft ist
- Aktive Phase: Nachrichten fließen korrekt über den etablierten WebSocket-Kanal
- Timeout-Auslöser: Nach ~10-15 Minuten läuft die serverseitige Sitzung wegen Inaktivitäts-Timeout ab
- WebSocket-Schließung: Server schließt WebSocket mit Code
1006oder sendetSession expired-Frame - Stille Wiederverbindung: Client versucht Reconnect, nimmt aber KEIN Auth-Token in den Wiederverbindungs-Handshake auf
- Auth-Fehler: Neue WebSocket-Verbindung wird mit
401abgelehnt, da der Sitzungsspeicher leer ist
Codepfad-Analyse
Der Fehler befindet sich in der Wiederverbindungslogik des Clients:
// Hypothetischer problematischer Code in web-ui/src/services/connection.ts
class ConnectionManager {
async reconnect() {
// ❌ FEHLER: Ruft Token nicht aus localStorage ab
const ws = new WebSocket(this.gatewayUrl);
ws.onopen = () => {
// Fehlt: this.authenticate();
};
}
// Korrekte Implementierung würde beinhalten:
async authenticate() {
const token = localStorage.getItem('openclaw_auth_token');
this.ws.send(JSON.stringify({
type: 'AUTH',
token: token // ← Dieser Schritt fehlt bei reconnect
}));
}
}
Sitzungsverwaltungs-Diskrepanz
Das Problem wird durch eine Versionsabweichung verschärft:
CLI Version: 2026.2.23
Browser Version: 2026.2.17
Dies deutet darauf hin, dass das Gateway möglicherweise separat von den eingebetteten Web-UI-Assets aktualisiert wurde, was dazu führen kann, dass die Sitzungsvalidierungslogik zwischen Server und Client divergiert.
Umgebungsbezogene Faktoren
| Faktor | Auswirkung |
|---|---|
GATEWAY_SESSION_TIMEOUT | Standard 600 Sekunden (10 Min.) |
GATEWAY_WEBSOCKET_PING_INTERVAL | Möglicherweise nicht konfiguriert, verursacht TCP-Keepalive-Lücken |
| Browser-Tab-Hintergrundung | Browser können WebSocket in Hintergrund-Tabs drosseln |
| macOS-Energieverwaltung | Kann Tab-Aktivität nach Display-Schlaf aussetzen |
🛠️ Schritt-für-Schritt-Lösung
Option 1: Client-Side-Fix (Sofortig - Für Benutzer)
Voraussetzung: Browser DevTools-Zugriff (F12)
- Öffnen Sie die Browser DevTools (F12)
- Navigieren Sie zum Tab Konsole
- Führen Sie das folgende Snippet vor der Inaktivitätsperiode aus:
// 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');
})();
Option 2: Serverkonfiguration (Für Administratoren)
Schritt 1: Gateway-Konfigurationsdatei finden:
# Linux/macOS
~/.openclaw/gateway.yaml
# Docker
docker exec openclaw-gateway cat /app/config/gateway.yaml
Schritt 2: Sitzungs-Timeout-Einstellungen ändern:
# Vorher (gateway.yaml)
server:
session_timeout: 600 # 10 minutes
# Nachher (gateway.yaml)
server:
session_timeout: 28800 # 8 hours
websocket:
ping_interval: 30 # Ping alle 30 Sekunden senden
ping_timeout: 10 # Trennen, wenn kein Pong innerhalb von 10 Sekunden
auth:
token_refresh_interval: 300 # Token automatisch alle 5 Minuten erneuern
Schritt 3: Gateway-Dienst neu starten:
# Systemd
sudo systemctl restart openclaw-gateway
# Docker
docker restart openclaw-gateway
# Direktes Binary
./openclaw gateway restart
Option 3: Web-UI-Code-Fix (Für Entwickler)
Datei: web-ui/src/services/WebSocketManager.ts
// Vorher (defekte Wiederverbindungslogik)
class WebSocketManager {
private handleReconnect() {
this.socket = new WebSocket(this.url);
// Fehlt: Authentifizierung bei neuer Verbindung
}
}
// Nachher (korrigierte Implementierung)
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
}
});
}
}
}
Zusätzlicher Fix für Session Store:
Datei: gateway/src/session/SessionStore.ts
// Vorher: Sitzungen laufen nur aufgrund von Timeout ab
async createSession(token: string): Promise {
return this.sessions.create({
token,
expiresAt: Date.now() + SESSION_TIMEOUT
});
}
// Nachher: Sitzungen können über Keepalive verlängert werden
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);
}
}
Option 4: Versionssynchronisierung (Für Versionskonflikt-Bug)
# Alle OpenClaw-Dienste stoppen
openclaw stop --all
# Zwischengespeicherte Assets löschen
rm -rf ~/.openclaw/cache/web-ui/
rm -rf ~/.openclaw/cache/assets/
# Neuinstallation zur Versionssynchronisierung
openclaw update --force
# Dienste neu starten
openclaw start --all
🧪 Verifizierung
Testfall 1: Basis-Wiederverbindung
# Terminal 1: Gateway-Logs überwachen
openclaw logs --follow gateway
# Browser: DevTools-Konsole öffnen und nach "OpenClaw" filtern
# Ausführen: Tab genau 12 Minuten lang inaktiv lassen
# Erwartet: Keine 401-Fehler nach Wiederverbindung
# Konsole sollte zeigen:
# [OpenClaw] Connection lost
# [OpenClaw] Reconnecting...
# [OpenClaw] Auth sent
# [OpenClaw] Reconnected successfully
Testfall 2: WebSocket-Ping/Pong-Verifizierung
# Im Browser-Consale Ping/Pong-Verkehr verifizieren
setInterval(() => {
console.table({
wsState: WebSocket.CONNECTING, // 0
wsOpen: WebSocket.OPEN, // 1
wsClosing: WebSocket.CLOSING, // 2
wsClosed: WebSocket.CLOSED // 3
});
}, 60000); // Alle Minute prüfen
# Erwartet: Status bleibt OPEN während der Hintergrundung
Testfall 3: Sitzungspersistenzprüfung
# Sitzungs-TTL über Gateway-API prüfen
curl -s http://127.0.0.1:18789/api/v1/session/status \
-H "Authorization: Bearer $(cat ~/.openclaw/auth_token)" \
| jq .session
# Erwartete Ausgabe:
# {
# "sessionId": "sess_abc123",
# "expiresAt": "2026-01-16T00:00:00Z",
# "ttl": 28800,
# "extendable": true
# }
Testfall 4: Lasttest (Automatisiert)
# Testsuite für Sitzungs-Timeout ausführen
npm test -- --grep "session-timeout"
# Erwartete Ergebnisse:
# ✓ reconnect-with-auth: Keine 401 nach 15 Min. Inaktivität
# ✓ token-refresh: Token wurde vor Ablauf automatisch erneuert
# ✓ multiple-reconnects: Auth über 5 Wiederverbindungszyklen hinweg bewahrt
Verifizierungs-Checkliste
| Test | Befehl | Erwartetes Ergebnis |
|---|---|---|
| Gateway erreichbar | curl http://127.0.0.1:18789/health | {“status”: “ok”} |
| WebSocket-Upgrade | websocat ws://127.0.0.1:18789/ws | Verbindung hergestellt |
| Auth-Token gültig | localStorage.openclaw_auth_token prüfen | Nicht-leerer JWT-String |
| Versionssync | openclaw version | Entspricht Browser-UI-Version |
⚠️ Häufige Fehler
Fallstrick 1: Browser-Datenschutz/Inkognito-Modus
Problem: localStorage wird bei Verwendung des Inkognito-/Privaten Browsens im strengen Modus gelöscht.
Symptom: Authentifizierung schlägt selbst nach Seitenaktualisierung fehl.
Workaround:
# Prüfen, ob localStorage zugänglich ist
console.log(localStorage.getItem('openclaw_auth_token'));
// Wenn null im Inkognito: Persistente Speicheroption in Einstellungen verwenden
Fallstrick 2: Docker-Netzwerksegmentierung
Problem: WebSocket-Verbindungen vom Browser können sich anders durch Docks internales NetzwerkRouting als HTTP.
Symptom: HTTP 401 trotz korrektem Token; WebSocket-Upgrade schlägt fehl.
Diagnose:
# Docker-Portzuordnungen prüfen
docker port openclaw-gateway
# Verifizieren, dass WebSocket-Endpunkt exponiert ist
curl -I http://localhost:18789/ws
# Sollte zurückgeben: 101 Switching Protocols
Fix:
# docker-compose.yaml Ergänzung
services:
gateway:
ports:
- "18789:18789" # HTTP/REST
- "18790:18790" # WebSocket (falls separrat)
Fallstrick 3: macOS Akku/Energiesparmodus
Problem: macOS kann die JavaScript-Ausführung in Hintergrund-Tabs drosseln und verhindert so Ping/Pong-Keepalive.
Symptom: Sitzungen laufen ab, selbst mit kurzem konfiguriertem Timeout.
Workaround:
# App Nap für Browser deaktivieren
# Safari: Entwickler → Experimentelle Funktionen → Hintergrund-Timer-Drosselung deaktivieren
# Chrome: --disable-background-timer-throttling Flag hinzufügen
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-background-timer-throttling
Fallstrick 4: Reverse-Proxy-Timeout
Problem: Nginx/Apache kann WebSocket-Verbindungen wegen proxy_read_timeout schließen.
Symptom: 401 erscheint genau beim Proxy-Timeout, nicht beim Gateway-Timeout.
Fix:
# 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 Stunden
proxy_send_timeout 86400;
}
Fallstrick 5: Authentifizierung mit mehreren Tabs
Problem: Das Öffnen mehrerer Tabs erstellt mehrere Sitzungen; das Schließen eines Tabs kann andere invalidieren, wenn der Einzelsitzungsmodus verwendet wird.
Symptom: 401 erscheint nach dem Öffnen eines zweiten Tabs und dessen Schließen.
Workaround: Multi-Session-Modus in der Gateway-Konfiguration aktivieren:
# gateway.yaml
auth:
allow_concurrent_sessions: true
session_mode: per-tab # Anstatt per-user
Fallstrick 6: Token-Ablauf vs. Sitzungsablauf
Problem: JWT kann unabhängig von der WebSocket-Sitzung ablaufen.
Symptom: 401 selbst unmittelbar nach der Authentifizierung.
Diagnose:
# JWT dekodieren, um Ablaufzeit zu prüfen
atob(localStorage.openclaw_auth_token.split('.')[1])
# "exp" Claim suchen - Unix-Zeitstempel
# Mit aktueller Zeit vergleichen
date +%s
🔗 Zugehörige Fehler
Zugehörige HTTP-Fehler
| Fehlercode | Problem | Verbindung |
|---|---|---|
401 Unauthorized | Ungültiges/abgelaufenes Authentifizierungstoken | Direkter Nachfolger dieses Problems |
403 Forbidden | Gültiges Token aber unzureichende Berechtigungen | Verwandter Authentifizierungsablauf |
407 Proxy Authentication Required | Proxy-Anmeldedaten erforderlich | Andere Schicht |
Zugehörige WebSocket-Schließcodes
| Schließcode | Name | Beschreibung |
|---|---|---|
1000 | Normal Closure | Beabsichtigtes Trennen |
1001 | Going Away | Server fährt herunter |
1006 | Abnormal Closure | Netzwerkfehler oder Timeout (unser Fall) |
1011 | Unexpected Error | Serverseitiger Fehler |
4001 | Authentication Failed | Benutzerdefinierter Gateway-Code |
Historische Probleme in OpenClaw
- Issue #2847: "WebSocket trennt zufällig während langer Konversationen" - Ähnlicher Timeout-Mechanismus
- Issue #2901: "Auth-Token wird nicht über Browser-Neustart hinweg beibehalten" - localStorage-Grenzfall
- Issue #3102: "Versionskonflikt zwischen CLI und Web-UI" - Verwandt mit der in diesem Bericht festgestellten Versionsdiskrepanz
- Issue #3156: "Dashboard erfordert alle 5 Minuten erneute Anmeldung" - Variante mit kürzerem Timeout
- Issue #3224: "WebSocket-Ping funktioniert nicht in Hintergrund-Tabs" - macOS-spezifisch
Zugehörige Dokumentation
- OpenClaw-Authentifizierungsarchitektur
- WebSocket-Protokollspezifikation
- Sitzungsveraltungskonfiguration
- Allgemeine Fehlerbehebungsanleitung
Bekannte betroffene Versionen
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)