Skip to content

fix(autopilot): cd into --repo so Bun auto-loads brain/.env#464

Open
notjbg wants to merge 1 commit into
garrytan:masterfrom
notjbg:fix/autopilot-cd-repo
Open

fix(autopilot): cd into --repo so Bun auto-loads brain/.env#464
notjbg wants to merge 1 commit into
garrytan:masterfrom
notjbg:fix/autopilot-cd-repo

Conversation

@notjbg

@notjbg notjbg commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Symptom

Autopilot looks healthy. gbrain autopilot --status returns the cheerful [cycle] score=N elapsed=1s next=150s line. autopilot.log shows cycles dispatching every 5 minutes. gbrain doctor stays at 90/100. But Embedded 0 chunks across N pages on every cycle, and the real story is buried in ~/.gbrain/autopilot.err:

Error embedding inbox/2026-04-23-...: The OPENAI_API_KEY environment variable is missing or empty
Error embedding inbox/2026-04-23-...: The OPENAI_API_KEY environment variable is missing or empty
... (3.4 MB of these in my case, accumulated over weeks)

Per-page embed failures are caught and warn-logged rather than propagated, so the cycle exits status=success with chunks_embedded: 0. No healthcheck flags it. New pages stop being searchable; nothing tells you.

Root cause

writeWrapperScript() (src/commands/autopilot.ts) generates this wrapper:

source ~/.zshrc 2>/dev/null || source ~/.bashrc 2>/dev/null || true
exec '...gbrain/src/cli.ts' autopilot --repo '/path/to/brain'

It sources the shell profile to pick up API keys exported there. That works for users who keep OPENAI_API_KEY in ~/.zshrc. But Bun also auto-loads .env from CWD (the whole point of "no dotenv needed" in a Bun project), and many users put their gbrain secrets in brain/.env — alongside the brain content, not in shell-init. Since launchd starts the wrapper with CWD = / or $HOME (not --repo), Bun's auto-load looks in the wrong directory and the daemon never sees brain/.env.

The interactive gbrain invocation works because users typically run it from inside the brain repo, where Bun does find brain/.env. So this only bites the daemon path, and only for users whose keys are in brain/.env.

Fix

cd '\${safeRepoPath}' || exit 1 before exec. Bun's CWD-based auto-load now finds brain/.env on every wrapper invocation. Existing shell-init env vars still take precedence (Bun's auto-load doesn't overwrite), so users with keys in ~/.zshrc are unaffected.

writeWrapperScript() is shared across launchd / systemd / crontab / ephemeral installers, so one site covers all four.

Repro

  1. Move OPENAI_API_KEY from ~/.zshrc to ~/brain/.env. Confirm in a fresh shell that echo \$OPENAI_API_KEY is empty.
  2. `gbrain autopilot --install --repo ~/brain`
  3. Wait one cycle (5 min default), then `tail ~/.gbrain/autopilot.err` — embed errors flood.
  4. Apply this PR's fix, `gbrain autopilot --install --repo ~/brain` to regenerate the wrapper, kickstart, wait one cycle. `tail ~/.gbrain/autopilot.log` shows `Embedded N chunks` and `autopilot.err` is quiet.

I confirmed the fix locally by patching ~/.gbrain/autopilot-run.sh directly and checking ps eww on the spawned Minions worker — OPENAI_API_KEY was visible in the worker child's env after the fix, absent before.

Possible follow-ups (not in this PR)

  • gbrain autopilot could also read --repo/.env itself at startup, so the fix works regardless of how the daemon is invoked. Wider blast radius — left for a separate change.
  • gbrain doctor could aggregate per-page embed failures into a warn-status check when failure rate exceeds a threshold. Would have caught this within one cycle. Happy to file a separate issue if there's interest.

Tests

No new test in this PR — writeWrapperScript is a string-template function and the change is two lines of generated shell. Happy to add a snapshot test if reviewers prefer.

The wrapper sources ~/.zshrc for API keys, but launchd (and systemd, and
crontab) start it with CWD = / or $HOME. Bun auto-loads .env from CWD, so
users who keep secrets in brain/.env (a natural Bun pattern, no dotenv
package needed) never have those vars visible to the daemon.

The symptom is silent: cycles look healthy in autopilot.log
([cycle] score=N elapsed=1s next=150s) and gbrain doctor stays at 90/100,
but autopilot.err fills with per-page "OPENAI_API_KEY missing" errors
and Embedded 0 chunks across N pages on every cycle. Per-page embed
errors are caught and warn-logged instead of failing the cycle, so no
healthcheck flags it.

Single-line fix: cd into --repo before exec. Bun finds brain/.env on the
next process startup. Existing shell-init env vars still take precedence
(Bun's auto-load doesn't overwrite), so users with keys in ~/.zshrc are
unaffected.

writeWrapperScript() is shared across all four installers (launchd,
systemd, crontab, ephemeral) so this one site covers everything.
@notjbg

notjbg commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

Friendly bump 🙏 — this regression bit me again today (2026-05-27 on v0.41.18.0). Daemon worker had zero API keys in env (verified via ps eww); Bun was not auto-loading ~/brain/.env because the launchd wrapper doesn't cd into --repo. 3 days of cascading silent failures: 5,720 apiKey/authToken errors in ~/.gbrain/autopilot.err (21 MB log), sync stalled at 62h freshness, all scheduled subagent jobs (daily journal, weekly review) permanently failed, while gbrain autopilot --status reported "installed" and gbrain doctor reported 90/100.

Applied this PR's diff locally to autopilot-run.sh (one cd line) → daemon respawned, env propagated, sync caught up in 36s (ingest +8 ~15 pages, 116 chunks), doctor went 90 → 95.

The current upstream template at autopilot.ts:790-808 sources ~/.zshenv + ~/.zshrc, which assumes API keys live in shell rc files. For setups where secrets live only in ~/brain/.env (working-tree-local + gitignored, the Bun-idiomatic pattern), the wrapper still has no path to load them. cd '\${safeRepoPath}' before exec is the minimal fix that covers both setups: zshenv-stored keys keep working, and Bun's .env auto-load picks up brain-local keys.

CI's green. Happy to add a test asserting the install template emits the cd line if that helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant