Summary
On Windows, the reflexio backend's publish_interaction endpoint (POST to http://localhost:8071/) never returns. The client blocks on the HTTP round-trip until it read-times-out; the interaction is never persisted, so nothing ever reaches the dashboard even when the backend is otherwise healthy.
Crucially, read endpoints keep answering 200 the entire time the write is hung — so the backend is up and serving, only the write path is wedged. This looks like a SQLite write-path deadlock / writer starvation.
Distinct from #107 (entrypoint allowlist) and #108 (stale backend blocking restart): here the backend is running and healthy for reads, correctly reached, correct entrypoint — the publish handler itself hangs.
Environment
Reproduction
Minimal, isolated from the hook — direct SDK call with wait_for_response=False:
import time
from reflexio.client.client import ReflexioClient
c = ReflexioClient(url_endpoint="http://localhost:8071/", timeout=65)
t = time.time()
c.publish_interaction(
user_id="VS Code",
interactions=[
{"role": "user", "content": "diag ping"},
{"role": "assistant", "content": "diag pong"},
],
session_id="diag",
wait_for_response=False,
)
print("%.1fs" % (time.time() - t))
Result:
ERR 67.08s: ReadTimeout(HTTPConnectionPool(host='localhost', port=8071):
Read timed out. (read timeout=65))
Per the SDK docstring, wait_for_response=False should return in ~100 ms after registering a BackgroundTask. It instead hangs indefinitely (reproduced at 5 s, 30 s, 60 s client timeouts — always times out).
Evidence it's the write path specifically, not the whole backend
- Read endpoints answer
200 continuously during the hung publish (from the dashboard's own polling in backend.log):
POST /api/get_user_playbooks 200 OK
POST /api/get_agent_playbooks 200 OK
GET /api/get_all_profiles 200 OK
GET /stall_state 200 OK
- The
POST /publish... request never produces a completion log line in backend.log (uvicorn logs on response completion → a hung handler never logs).
- Via the Stop hook the same failure surfaces as, in
~/.claude-smart/hook.log:
{"event":"stop","internal_skipped":false,"handler_status":"ok",
"publish_status":"failed","publish_count":2}
and on stderr: publish_interaction failed: HTTPConnectionPool(host='localhost', port=8071): Read timed out.
- DB row counts stay
0 after every attempt (interactions, requests, profiles).
Correlated DB state
~/.reflexio/data/:
reflexio.db 4 KB
reflexio.db-shm 32 KB
reflexio.db-wal ~2.6 MB <-- large, never checkpointed
The WAL is multi-MB and never checkpoints while the dashboard polls continuously. Consistent with a long-lived reader snapshot preventing WAL checkpoint and a writer blocked on the write lock. Windows SQLite file-locking is stricter than POSIX, which may explain why this manifests here.
Additionally, once a publish has hung, backend restart itself hangs (startup DB init appears to block the same way), which compounds recovery.
Expected
publish_interaction(wait_for_response=False) returns ~200 promptly; the interaction is persisted and the WAL checkpoints; the dashboard shows the entry.
Notes / possible directions
- Confirm whether the publish handler holds a synchronous SQLite write while the event loop keeps serving async reads (thread-pool writer blocked on the lock).
- Check WAL checkpoint policy under continuous read polling on Windows (
PRAGMA wal_autocheckpoint, busy_timeout, checkpoint-on-write).
- A
busy_timeout on the writer + a bounded publish handler timeout would at least turn the silent hang into a fast, loggable error.
Happy to run any additional probes or capture a stack dump of the backend while it's wedged.
Summary
On Windows, the reflexio backend's
publish_interactionendpoint (POSTtohttp://localhost:8071/) never returns. The client blocks on the HTTP round-trip until it read-times-out; the interaction is never persisted, so nothing ever reaches the dashboard even when the backend is otherwise healthy.Crucially, read endpoints keep answering
200the entire time the write is hung — so the backend is up and serving, only the write path is wedged. This looks like a SQLite write-path deadlock / writer starvation.Distinct from #107 (entrypoint allowlist) and #108 (stale backend blocking restart): here the backend is running and healthy for reads, correctly reached, correct entrypoint — the
publishhandler itself hangs.Environment
CLAUDE_CODE_ENTRYPOINT=claude-vscode)CLAUDE_SMART_USE_LOCAL_CLI=1,CLAUDE_SMART_USE_LOCAL_EMBEDDING=1,CLAUDE_SMART_READ_ONLY=0:8071, embedding on:8072(both healthy:/health→ 200)Reproduction
Minimal, isolated from the hook — direct SDK call with
wait_for_response=False:Result:
Per the SDK docstring,
wait_for_response=Falseshould return in ~100 ms after registering aBackgroundTask. It instead hangs indefinitely (reproduced at 5 s, 30 s, 60 s client timeouts — always times out).Evidence it's the write path specifically, not the whole backend
200continuously during the hung publish (from the dashboard's own polling inbackend.log):POST /publish...request never produces a completion log line inbackend.log(uvicorn logs on response completion → a hung handler never logs).~/.claude-smart/hook.log:{"event":"stop","internal_skipped":false,"handler_status":"ok", "publish_status":"failed","publish_count":2}publish_interaction failed: HTTPConnectionPool(host='localhost', port=8071): Read timed out.0after every attempt (interactions,requests,profiles).Correlated DB state
~/.reflexio/data/:The WAL is multi-MB and never checkpoints while the dashboard polls continuously. Consistent with a long-lived reader snapshot preventing WAL checkpoint and a writer blocked on the write lock. Windows SQLite file-locking is stricter than POSIX, which may explain why this manifests here.
Additionally, once a publish has hung, backend restart itself hangs (startup DB init appears to block the same way), which compounds recovery.
Expected
publish_interaction(wait_for_response=False)returns ~200 promptly; the interaction is persisted and the WAL checkpoints; the dashboard shows the entry.Notes / possible directions
PRAGMA wal_autocheckpoint,busy_timeout, checkpoint-on-write).busy_timeouton the writer + a bounded publish handler timeout would at least turn the silent hang into a fast, loggable error.Happy to run any additional probes or capture a stack dump of the backend while it's wedged.