gbrain version: 0.41.0.0
Engine: Postgres (local standalone via Homebrew)
Runtime: Bun 1.2.22
OS: macOS (arm64)
Description
Running gbrain dream runs all 17 phases, but every DB-dependent phase fails with "No database connection: connect() has not been called". Filesystem-only phases (lint, backlinks) work fine.
Reproduction
Output (abbreviated):
! lint 0 fix(es) applied, 52 remaining ← OK (FS-only)
✓ backlinks 1 missing back-link(s) found ← OK (FS-only)
✗ sync sync phase failed ← FAIL
[InternalError/UNKNOWN] No database connection: connect() has not been called.
✗ synthesize synthesize phase failed ← FAIL
...
✓ extract 0 link(s), 0 timeline entries ← batch writes silently lost
✗ embed embed phase failed ← FAIL
✗ orphans orphans phase failed ← FAIL
...
Key Observations
-
Each individual phase works fine in isolation:
gbrain dream --phase sync # ✅ works
gbrain dream --phase embed # ✅ works
gbrain dream --phase extract # ✅ works
gbrain dream --phase orphans # ✅ works
-
gbrain doctor connects fine — all 47 checks pass, 100/100 score.
-
gbrain init --url <url> connects fine — schema migration succeeds.
-
Programmatic runCycle(engine, ...) also fails when running all phases, but a single-phase (['extract']) run works.
-
GBRAIN_POOL_SIZE=2 doesn't help — the CLI's toEngineConfig() doesn't pass poolSize to the engine config, so the engine always runs in module mode relying on the global db.sql singleton.
Root Cause Analysis
The error originates from src/core/db.ts:getConnection():
export function getConnection() {
if (!sql) {
throw new GBrainError(
'No database connection',
'connect() has not been called',
...
);
}
return sql;
}
The module-level sql variable is set by connect() at startup but becomes null by the time any DB phase executes in the full cycle. Individual phases (separate process) work because each gets a fresh module instance.
Likely cause: The full cycle uses ~20 dynamic await import(...) calls for phase implementations. In some runtime configurations, these dynamic imports can create separate module scopes for db.ts, causing the sql variable to be shadowed or cleared. The PostgresEngine.sql getter and db.getConnection() both check a module-level sql that's null in a different module scope.
Affected code paths
src/core/postgres-engine.ts:120 — get sql() falls back to db.getConnection()
src/core/db.ts:151-156 — getConnection() throws when sql is null
src/core/cycle.ts — full cycle with 17 phases, uses await import() for each phase module
src/cli.ts:1007-1020 — dream handler: connectEngine() → runDream(eng) → disconnect()
Workaround
Run individual maintenance steps directly — they all work:
gbrain sync --repo ~/brain
gbrain embed --stale
gbrain check-backlinks fix ~/brain
gbrain lint
gbrain extract all
Suggested Fix
- Have
PostgresEngine.connect() always create this._sql (instance connection) instead of relying on the module-level db.sql singleton, OR
- Ensure dynamic imports in the cycle don't trigger re-initialization of module-level state, OR
- Re-check
db.sql freshness before each phase and reconnect if stale.
Full diagnostic data
gbrain doctor --json: all 47 checks pass, 100/100 score
gbrain config show: engine=postgres, database_url=postgresql://localhost:5432/gbrain
- Fresh re-init + re-import confirmed the bug is not data/config-dependent
- Migrations: all 89 applied, schema v93 (latest)
gbrain version: 0.41.0.0
Engine: Postgres (local standalone via Homebrew)
Runtime: Bun 1.2.22
OS: macOS (arm64)
Description
Running
gbrain dreamruns all 17 phases, but every DB-dependent phase fails with"No database connection: connect() has not been called". Filesystem-only phases (lint, backlinks) work fine.Reproduction
Output (abbreviated):
Key Observations
Each individual phase works fine in isolation:
gbrain doctorconnects fine — all 47 checks pass, 100/100 score.gbrain init --url <url>connects fine — schema migration succeeds.Programmatic
runCycle(engine, ...)also fails when running all phases, but a single-phase (['extract']) run works.GBRAIN_POOL_SIZE=2doesn't help — the CLI'stoEngineConfig()doesn't passpoolSizeto the engine config, so the engine always runs in module mode relying on the globaldb.sqlsingleton.Root Cause Analysis
The error originates from
src/core/db.ts:getConnection():The module-level
sqlvariable is set byconnect()at startup but becomesnullby the time any DB phase executes in the full cycle. Individual phases (separate process) work because each gets a fresh module instance.Likely cause: The full cycle uses ~20 dynamic
await import(...)calls for phase implementations. In some runtime configurations, these dynamic imports can create separate module scopes fordb.ts, causing thesqlvariable to be shadowed or cleared. ThePostgresEngine.sqlgetter anddb.getConnection()both check a module-levelsqlthat's null in a different module scope.Affected code paths
src/core/postgres-engine.ts:120—get sql()falls back todb.getConnection()src/core/db.ts:151-156—getConnection()throws whensqlis nullsrc/core/cycle.ts— full cycle with 17 phases, usesawait import()for each phase modulesrc/cli.ts:1007-1020— dream handler:connectEngine()→runDream(eng)→disconnect()Workaround
Run individual maintenance steps directly — they all work:
Suggested Fix
PostgresEngine.connect()always createthis._sql(instance connection) instead of relying on the module-leveldb.sqlsingleton, ORdb.sqlfreshness before each phase and reconnect if stale.Full diagnostic data
gbrain doctor --json: all 47 checks pass, 100/100 scoregbrain config show: engine=postgres, database_url=postgresql://localhost:5432/gbrain