Skip to content

gstack-gbrain-sync: sourceLocalPath() expects bare array but gbrain ≥0.19 returns { sources: [...] } → fatal 'list.find is not a function' #1567

@kylma-code

Description

@kylma-code

Summary

/sync-gbrain crashes fatally on the first run with:

gstack-gbrain-sync fatal: list.find is not a function. (In 'list.find((s) => s.id === sourceId)', 'list.find' is undefined)

The orchestrator never reaches any stage (code/memory/brain-sync) — it dies during planning.

Environment

  • gstack: 1.40.0.0
  • gbrain: 0.35.4.0 (engine: pglite, local-stdio)
  • macOS (darwin arm64), bun 1.3.11
  • Repo: a normal local git repo, freshly git init'd, source not yet registered

Root cause

bin/gstack-gbrain-sync.tssourceLocalPath() (around line 292) calls
gbrain sources list --json and types/treats the result as a bare array:

const list = execGbrainJson<Array<{ id: string; local_path?: string }>>(
  ["sources", "list", "--json"],
  { baseEnv: env },
);
if (!list) return null;
const found = list.find((s) => s.id === sourceId);   // ← list is not an array

But gbrain >= 0.19 wraps the listing in an object:

{ "sources": [ { "id": "default", "local_path": null, "page_count": 99, ... } ] }

So list.find is undefined → fatal. gstack pins gbrain 0.18.2
(bin/gstack-gbrain-install PINNED_COMMIT), so this only bites users who run
a newer gbrain — which is increasingly common since gbrain's own update path
moves ahead of the gstack pin.

Repro

  1. Run any gbrain >= 0.19 (gbrain --version).
  2. In a git repo whose code source is not yet registered, run /sync-gbrain
    (or bun run ~/.claude/skills/gstack/bin/gstack-gbrain-sync.ts).
  3. Fatal: list.find is not a function.

Fix (verified locally)

Accept both the legacy array and the new { sources: [...] } shape:

const raw = execGbrainJson<
  | Array<{ id: string; local_path?: string }>
  | { sources?: Array<{ id: string; local_path?: string }> }
>(["sources", "list", "--json"], { baseEnv: env });
if (!raw) return null;
// gbrain <=0.18 returned a bare array; >=0.19 wraps it as { sources: [...] }.
const list = Array.isArray(raw) ? raw : (raw.sources ?? []);
const found = list.find((s) => s.id === sourceId);
return found?.local_path ?? null;

After this patch, /sync-gbrain runs 3/3 stages OK against gbrain 0.35.4.0
(code source registered + synced, memory import, brain-sync push).

sourceLocalPath() is the only execGbrainJson consumer in the file; other
gbrain interactions use spawnGbrain (exit-code based) and are unaffected.
Note /sync-gbrain SKILL.md Step 3 already does ... | jq '.sources[]',
so the wrapped shape is the expected one — only this helper was missed.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions