fix: stale Claude-Session-ID automatisch resetten [appimage]
All checks were successful
Build AppImage / build (push) Successful in 6m20s

Wenn die in SQLite gespeicherte claude_session_id keine passende
Konversation mehr hat (neue Maschine, Cache geleert, frische Installation),
warf das SDK "No conversation found with session ID: <uuid>" und der
Chat blieb kaputt — der Retry in claude-bridge.js hat NIE gegriffen, weil
er auf queryOptions.sessionId geprueft hat waehrend wir queryOptions.resume
setzen.

Fix:
- claude-bridge.js: Retry-Guard auf queryOptions.resume umgezogen + Match
  auf die konkrete Fehlermeldung ("No conversation found with session ID").
  Bei stale ID: session-reset-Event an Rust senden, dann retry ohne resume.
- claude.rs: Neuer Handler fuer session-reset — loescht die stale
  claude_session_id aus der aktiven Session in der DB, damit die App beim
  naechsten Start nicht wieder in denselben Fehler laeuft.

Reproduziert auf VM + NixOS-Desktop nach frischer Installation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Eddy 2026-04-20 17:16:35 +02:00
parent 3913e1cce8
commit 2e473cde00
2 changed files with 33 additions and 6 deletions

View file

@ -481,18 +481,26 @@ async function sendMessage(message, requestId, model = null, contextOverride = n
});
}
// Hilfsfunktion: Iteration mit Fallback bei ungueltiger Session-ID
// Hilfsfunktion: Iteration mit Fallback bei ungueltiger Session-ID.
// Typischer Auslöser: "No conversation found with session ID: <uuid>"
// passiert wenn die in SQLite gespeicherte Claude-Session lokal fehlt
// (neue Maschine, Projekt-Cache geloescht, frische Installation, ...).
async function* iterateWithRetry() {
try {
for await (const ev of conversation) yield ev;
} catch (err) {
// Wenn Resume-Session ungueltig → Retry ohne sessionId
if (queryOptions.sessionId) {
const msg = err?.message || String(err);
const isStaleSession = /no conversation found with session id/i.test(msg);
if (queryOptions.resume && isStaleSession) {
const staleId = queryOptions.resume;
// Rust-Seite informieren, damit die stale ID aus der DB geloescht
// wird — sonst probieren wir beim naechsten Start wieder dasselbe.
sendEvent('session-reset', { staleSessionId: staleId, reason: msg });
sendMonitorEvent('agent', 'Resume fehlgeschlagen, starte neue Session', {
reason: err.message || String(err),
oldSessionId: queryOptions.sessionId,
reason: msg,
oldSessionId: staleId,
});
delete queryOptions.sessionId;
delete queryOptions.resume;
conversation = query({ prompt: fullPrompt, options: queryOptions });
for await (const ev of conversation) yield ev;
} else {

View file

@ -256,6 +256,25 @@ fn handle_bridge_message(app: &AppHandle, msg: BridgeMessage) {
}
let _ = app.emit("claude-result", &payload);
}
"session-reset" => {
// Bridge meldet: resume-Session war ungueltig ("No conversation
// found with session ID"). Stale claude_session_id aus DB
// entfernen, sonst laeuft die App beim naechsten Start wieder
// in denselben Fehler.
if let Some(db_state) = app.try_state::<Arc<Mutex<db::Database>>>() {
let db_lock = db_state.lock().unwrap();
if let Ok(Some(active_id)) = db_lock.get_setting("active_session_id") {
if !active_id.is_empty() {
if let Ok(Some(mut session)) = db_lock.get_session(&active_id) {
let old = session.claude_session_id.take();
let _ = db_lock.update_session(&session);
println!("🧹 Stale claude_session_id geloescht: {:?}", old);
}
}
}
}
let _ = app.emit("session-reset", &payload);
}
"all-stopped" => {
println!("⏹️ Alle Agents gestoppt");
let _ = app.emit("all-stopped", ());