Sessions become unrevivable: a stale `inuse.<pid>.lock` from a crashed host is never reclaimed on open
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Shell
- Sterne
- 11.2k
- Forks
- 1.9k
- Ø Merge
- 14 Std. 16 Min.
- Gemergte PRs (30 T.)
- 6
Beschreibung
Describe the bug
Several saved sessions became impossible to reopen ("crashed out / unrevivable"). On investigation the session data was not corrupt — the append-only events.jsonl replayed cleanly and the engine logged a successful resume. The sessions were blocked by runtime/lifecycle issues, not data loss.
The primary cause is stale lock handling. Each live session writes ~/.copilot/session-state/<id>/inuse.<pid>.lock. When the owning copilot --server process exits uncleanly (crash, force-quit, OS kill), the lock file is left behind. On the next open, the session is treated as in-use / unrevivable instead of checking whether that PID is still alive.
Scanning one local store, 14 of 184 sessions (~8%) carried a fault signature; 8 held an inuse.<pid>.lock whose PID was dead. Deleting the stale lock made each session openable again, and the app then spawned a fresh host and resumed normally — confirming the data was fine and only the lock blocked recovery.
Three related lifecycle defects compound the impact (details under Additional context):
- Orphaned engine host after a failed UI attach — the engine resumes but no session host registers, leaving a live server holding the lock (which then becomes defect #1 on the next attempt).
- MCP OAuth timeouts block session readiness — resume stalls ~5 minutes on
OAuth callback timeoutbefore the session is interactive, so users abort. - Unbounded
events.jsonlmakes replay hang on large/old sessions (appears to overlap #4251).
Affected version
1.0.83 (copilot --server --stdio --no-auto-update)
Steps to reproduce the behavior
Primary defect (stale lock not reclaimed):
- Open a session, then hard-kill its
copilot --serverprocess to simulate a crash / force-quit (kill -9 <pid>). - Confirm
~/.copilot/session-state/<id>/inuse.<pid>.lockremains, now pointing at a dead PID. - Try to reopen the session → it does not revive.
rmthe stale lock file → the session reopens and resumes cleanly (a fresh host is spawned andevents.jsonlreplays without error).
Expected behavior
On open, if the lock's PID is dead or a zombie, the app should reclaim the session automatically rather than treating it as in-use. A liveness check combined with a PID start-time comparison avoids the classic PID-reuse race. Recoverable work should never be gated behind a leftover lock file that only manual filesystem surgery can clear.
Additional context
- OS: macOS · shell: zsh
Defect 2 — orphaned engine host after a failed UI attach. Engine log from a single resume: the host never registers, yet the engine reports the session resumed, so the process lingers as an orphan holding the lock:
[WARNING] [rust:copilot_runtime::session::pending_request_flow] pending-request event was not delivered to the session host
{"error":"GenericFailure, no session host is registered for session <id>"}
...
[INFO] Resumed session: <id> (logged repeatedly)
Suggested fix: tie the engine host's lifetime to successful UI host registration; if registration fails/times out, tear the host down and release the lock.
Defect 3 — MCP OAuth timeouts block resume (~5 min). With several MCP servers configured, resume stalls until interactive OAuth flows time out. MCP init begins at 20:27, first hard failures at 20:32:
[ERROR] [rust:mcp_engine::session_authorizer] OAuth login failed after returning its authorization URL
{"server_name":"...","error":"OAuth callback timeout"}
During that window the session looks hung and users abort — observed as sessions whose only recent events were resume → abort (user_initiated) → shutdown, twice in a row. Suggested fix: make MCP init lazy / non-blocking; never gate session interactivity on MCP OAuth.
Defect 4 — unbounded events.jsonl. The append-only log grows without bound; worst local session reached 38.6 MB / ~13k events with 42 compaction cycles (173 re-injected system.message events ≈ 10.7 MB). Reopening replays the whole log, so large sessions hang on open even though nothing is corrupt. This looks like the same root cause as #4251; noting here for linkage. Suggested fix: snapshot-and-truncate the event log at compaction so replay cost stays bounded.
Possibly related: #4020 (session falsely "already in use by another client"), #4098 (truncated/concatenated events on resume), #4138 (resume compaction hangs), #4251 (large-session resume OOM/hang).
All four are addressable without changing the on-disk event format. Defects 1–2 turn recoverable work into apparent data loss; 3–4 make large/older sessions feel dead on open.
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne damit, die session-open-Behandlung für ~/.copilot/session-state//inuse..lock und den Lebenszyklus von copilot --server nachzuverfolgen. Reproduziere den Fall eines harten Beendens und untersuche anschließend die Rust-Sitzungslaufzeit rund um die Rückforderung von Sperren und die Host-Registrierung; der primäre Fehler ist behoben, wenn eine tote oder Zombie-PID das erneute Öffnen nicht mehr blockiert und die Sitzung ohne manuelles Entfernen der Sperre fortgesetzt wird. Die Anliegen zu OAuth und events.jsonl sollten als separater Umfang behandelt werden.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- rust
- Bereich
- backend, cli
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 45/100