You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Same output as the #1964 family (#1792#1832#1969#2051), but with a root cause none of them cover:
gstack-gbrain-sync (incremental):
SKIP code skipped — local engine broken-config — config at ~/.gbrain/config.json is malformed; see /setup-gbrain Step 1.5 (0.9s)
SKIP memory skipped — local engine broken-config — config at ~/.gbrain/config.json is malformed; see /setup-gbrain Step 1.5
OK brain-sync curated artifacts pushed (2.8s)
Meanwhile the engine is healthy: gbrain doctor --fast --json answers with only warnings, and ~/.gbrain/config.json is valid, well-formed JSON (pglite engine, voyage-code-3 embeddings).
Root cause: gbrain serve holds the PGLite database exclusively
PGLite is an embedded single-process engine — whichever process opens ~/.gbrain/brain.pglite owns it. With the recommended default setup (/setup-gbrain → PGLite + local-stdio MCP), Claude Code spawns gbrain serve at session start, and serve opens the database eagerly at boot — even if no mcp__gbrain__* tool is ever called in the session:
$ pgrep -fl gbrain
95427 bun /Users/.../.bun/bin/gbrain serve # parent PID = the live `claude` process
$ lsof +D ~/.gbrain/brain.pglite | head -3
COMMAND PID USER FD TYPE ... NAME
bun 95427 yannpringault 6u REG ... .../brain.pglite/pg_wal/000000010000000000000004
bun 95427 yannpringault 7u REG ... .../brain.pglite/global/2672
Every gbrain CLI command that needs the DB then times out at connect:
$ gbrain sources list --json
gbrain sources: connect timed out (default 10000ms; pass --timeout=Ns to override).
$ echo $?
124
freshClassify in lib/gbrain-local-status.ts probes exactly that command. The failure matches none of its patterns — stderr contains neither "Cannot connect to database" nor "config.json", and the process wasn't killed by the classifier's own timer (gbrain exits 124 on its internal timeout, so e.killed/SIGTERM/ETIMEDOUT are all false) — and it falls through to the defensive default at lib/gbrain-local-status.ts:304: broken-config. The user is told their valid config is malformed.
Killing the serve process unblocks everything instantly — gbrain sources list --json returns in well under a second and the re-run syncs clean (OK code ... page_count=130). Which confirms it was a lock, not a slow engine.
Why this is distinct from #1964 (and why its fix makes this variant worse)
#1964's cause is a slow connection (Supabase transaction-mode pooler, 6.9–10.7s) — raising the probe timeout fixes it. This variant is a held lock: the probe can never succeed while any Claude session is alive, because serve spawns per-session and owns the engine for the session's lifetime. A longer timeout just makes /sync-gbrain wait longer before mislabeling the same lock.
It also hits harder than the pooler variant: it affects the default setup path (PGLite + local-stdio is what /setup-gbrain recommends), and it's deterministic from the second session onward. It only ever works on setup day because the first sync runs before a session restart spawns serve. Every /sync-gbrain run inside a live Claude session hits it.
Suggested fixes
Classifier (gstack, small): treat gbrain's own connect-timeout as its own state — match "connect timed out" in stderr and/or exit code 124 → a new engine-locked/timeout classification instead of the broken-config defensive default. Bonus: when the engine is pglite, check for a gbrain serve process (or lsof on the database path) and name the holding pid in the SKIP message — "database held by gbrain serve (pid N); stop it or run the sync outside a Claude session".
Upstream (gbrain): the CLI should either proxy through a running serve or fail with "database held by pid N" instead of a generic connect timeout. Happy to file that on the gbrain repo if useful — flagging it here first since gstack owns the misdiagnosis and the user-facing message.
MCP: local-stdio, registered user-scope in Claude Code (spawns gbrain serve per session)
Workaround that recurs every session: kill $(pgrep -f "gbrain serve") → GSTACK_DETECT_NO_CACHE=1 /sync-gbrain (the 60s classifier cache otherwise replays the stale negative)
Symptom
Same output as the #1964 family (
#1792#1832#1969#2051), but with a root cause none of them cover:Meanwhile the engine is healthy:
gbrain doctor --fast --jsonanswers with only warnings, and~/.gbrain/config.jsonis valid, well-formed JSON (pglite engine, voyage-code-3 embeddings).Root cause:
gbrain serveholds the PGLite database exclusivelyPGLite is an embedded single-process engine — whichever process opens
~/.gbrain/brain.pgliteowns it. With the recommended default setup (/setup-gbrain→ PGLite + local-stdio MCP), Claude Code spawnsgbrain serveat session start, and serve opens the database eagerly at boot — even if nomcp__gbrain__*tool is ever called in the session:Every gbrain CLI command that needs the DB then times out at connect:
freshClassifyinlib/gbrain-local-status.tsprobes exactly that command. The failure matches none of its patterns — stderr contains neither"Cannot connect to database"nor"config.json", and the process wasn't killed by the classifier's own timer (gbrain exits 124 on its internal timeout, soe.killed/SIGTERM/ETIMEDOUTare all false) — and it falls through to the defensive default atlib/gbrain-local-status.ts:304:broken-config. The user is told their valid config is malformed.Killing the serve process unblocks everything instantly —
gbrain sources list --jsonreturns in well under a second and the re-run syncs clean (OK code ... page_count=130). Which confirms it was a lock, not a slow engine.Why this is distinct from #1964 (and why its fix makes this variant worse)
#1964's cause is a slow connection (Supabase transaction-mode pooler, 6.9–10.7s) — raising the probe timeout fixes it. This variant is a held lock: the probe can never succeed while any Claude session is alive, because serve spawns per-session and owns the engine for the session's lifetime. A longer timeout just makes
/sync-gbrainwait longer before mislabeling the same lock.It also hits harder than the pooler variant: it affects the default setup path (PGLite + local-stdio is what
/setup-gbrainrecommends), and it's deterministic from the second session onward. It only ever works on setup day because the first sync runs before a session restart spawns serve. Every/sync-gbrainrun inside a live Claude session hits it.Suggested fixes
"connect timed out"in stderr and/or exit code 124 → a newengine-locked/timeoutclassification instead of thebroken-configdefensive default. Bonus: when the engine is pglite, check for agbrain serveprocess (orlsofon the database path) and name the holding pid in the SKIP message — "database held bygbrain serve(pid N); stop it or run the sync outside a Claude session"./sync-gbrain's safety note already warns about racinggbrain autopilot(/sync-gbraincan trigger gbrain's destructive auto-recovery, wiping user repos #1734); the session's own MCP serve process is the same class of conflict and deserves the same sentence.Environment
gbrain serveper session)kill $(pgrep -f "gbrain serve")→GSTACK_DETECT_NO_CACHE=1 /sync-gbrain(the 60s classifier cache otherwise replays the stale negative)