Summary
Two independent defects composed into a full Web GUI outage on 2026-09-13 (observed by the long-running GUI observer):
- daemon: a runtime DB "sidecar divergence" (deleted-open
-wal) makes the connection guard refuse every new connection, so all API endpoints fast-fail 500 until the daemon is restarted — a transient sidecar state escalates into a permanent self-locked outage with no recovery path.
- web-gui: while the daemon returned 500, the frontend hot-retried the failed agent detail load with zero backoff at ~209 req/s for 21 minutes (263,356 requests to a single endpoint from one page), amplifying the outage into a self-DDoS.
Timeline (UTC, 2026-09-13)
| Time |
Event |
| 05:00:02 |
First ERROR holon::runtime_db::connection: runtime db sidecar divergence error=runtime db sidecar divergence detected (deleted-open sidecar); refusing a new connection: db=/home/jolestar/.holon/state/runtime.sqlite, sidecar=-wal, fd=11, open_dev=66311, open_inode=89522540, canonical_identity=None (journal, daemon PID 2427897) |
| 05:00:03 → 05:21:10 |
Every API request needing a DB connection fast-fails 500 (durationMs=1). Observer recorded 263,378 × 500 + 113 × 503 (~209 rps sustained), dominated by GET /api/agents/holon-growth/skills (263,356) |
| 05:21:10 |
Operator stopped the daemon (systemd: "Consumed 9h 32min 48.144s CPU time" over a 1h32m uptime ≈ 6.2 cores average) |
| 05:30:30 |
Daemon restarted; /api/agents/holon-growth/skills returns 200 OK immediately |
Context: the daemon was already under heavy lock pressure from #2948 (memory indexer rebuild monopolization; database is locked warnings ongoing, ~200% CPU) in the same window.
Defect A (daemon): deleted-open sidecar → total connection refusal, no self-recovery
Defect B (web-gui): zero-backoff hot retry loop on failed agent loads
- During the 500 window one agent page issued 263,356 requests to
/api/agents/{id}/skills in 21 minutes (~209 rps, no sign of any backoff), each also emitting a console.error ("Failed to load resource: the server responded with a status of 500").
- Fetch path:
client.ts getSkillCatalog(agentId) (web-gui/app/src/runtime/client.ts:995), agent-scoped loads in web-gui/app/src/runtime/runtime-store.ts:2695/2738/2783 (error stored into agentSkillCatalogErrorByAgentId).
- Whatever the driving loop is (render-triggered refetch or catch-retry), a failing fetch must back off (exponential + jitter, capped) instead of hot-looping. The browser-side amplification contributed most of the 6-core CPU burn during the outage.
Evidence
- Observer JSONL:
events-2026-09-13.jsonl (527,636 lines; 263,491 http_error + 263,491 console, 1:1 paired).
journalctl --user -u holon.service excerpts 05:00:02Z–05:21Z.
- Post-restart curl:
GET /api/agents/holon-growth/skills → 200.
Fix directions
- A: root-cause the deleted-open
-wal (likely interplay with heavy WAL/checkpoint churn); consider a safe in-process recovery path (re-resolve canonical identity / reopen) for this divergence class, or at minimum degrade to a bounded refusal with explicit liveness signal instead of a silent permanent 500 wall.
- B: add bounded exponential backoff (with jitter) for agent detail / skills loads and audit other fetch loops for the same pattern.
Related: #2948 (indexer lock convoy active in the same window), #2940 (prior indexer pacing fix, partial).
Summary
Two independent defects composed into a full Web GUI outage on 2026-09-13 (observed by the long-running GUI observer):
-wal) makes the connection guard refuse every new connection, so all API endpoints fast-fail500until the daemon is restarted — a transient sidecar state escalates into a permanent self-locked outage with no recovery path.Timeline (UTC, 2026-09-13)
ERROR holon::runtime_db::connection: runtime db sidecar divergence error=runtime db sidecar divergence detected (deleted-open sidecar); refusing a new connection: db=/home/jolestar/.holon/state/runtime.sqlite, sidecar=-wal, fd=11, open_dev=66311, open_inode=89522540, canonical_identity=None(journal, daemon PID 2427897)500(durationMs=1). Observer recorded 263,378 × 500 + 113 × 503 (~209 rps sustained), dominated byGET /api/agents/holon-growth/skills(263,356)/api/agents/holon-growth/skillsreturns 200 OK immediatelyContext: the daemon was already under heavy lock pressure from #2948 (memory indexer rebuild monopolization;
database is lockedwarnings ongoing, ~200% CPU) in the same window.Defect A (daemon): deleted-open sidecar → total connection refusal, no self-recovery
src/runtime_db/connection.rs:418(refusing a new connection ... preserve the files and FD/inode evidence, then perform offline recovery or restart).-walwhile an fd still held it open (canonical_identity=Nonesuggests the open handle predates identity tracking). Candidate: WAL truncation/checkpoint churn under the Daemon memory indexer: one agent's inline full rebuild monopolizes the serial round loop — all cursors freeze and lag grows unbounded (#2940 follow-up) #2948 lock convoy.Defect B (web-gui): zero-backoff hot retry loop on failed agent loads
/api/agents/{id}/skillsin 21 minutes (~209 rps, no sign of any backoff), each also emitting aconsole.error("Failed to load resource: the server responded with a status of 500").client.ts getSkillCatalog(agentId)(web-gui/app/src/runtime/client.ts:995), agent-scoped loads inweb-gui/app/src/runtime/runtime-store.ts:2695/2738/2783(error stored intoagentSkillCatalogErrorByAgentId).Evidence
events-2026-09-13.jsonl(527,636 lines; 263,491http_error+ 263,491console, 1:1 paired).journalctl --user -u holon.serviceexcerpts 05:00:02Z–05:21Z.GET /api/agents/holon-growth/skills→ 200.Fix directions
-wal(likely interplay with heavy WAL/checkpoint churn); consider a safe in-process recovery path (re-resolve canonical identity / reopen) for this divergence class, or at minimum degrade to a bounded refusal with explicit liveness signal instead of a silent permanent 500 wall.Related: #2948 (indexer lock convoy active in the same window), #2940 (prior indexer pacing fix, partial).