garrytan/gstack · error · Error
gbrain sources list returned no JSON
Error message
gbrain sources list returned no JSON
What it means
Thrown by fetchSources() in gbrain-guards when execGbrainJson(['sources','list','--json']) returns null — meaning the gbrain CLI ran but emitted no JSON on stdout at all. This is a fail-closed signal: the destructive-op guards cannot prove a source row is safe without the list, so they refuse to proceed rather than treat an unreadable list as empty. Distinct from 'CLI not on PATH' (spawn failed) and 'non-JSON output' (stdout existed but unparseable).
Source
Thrown at lib/gbrain-guards.ts:202
_keepStorageMemo = { key, value };
return value;
}
/** Test-only: reset the per-process capability memo. */
export function _resetCapabilityMemo(): void {
_keepStorageMemo = undefined;
}
// ── Destructive-op decisions ────────────────────────────────────────────────
/**
* Fetch + normalize the source list. Throws on read/parse failure so callers can
* distinguish "couldn't read" (fail closed) from "empty list" (source absent).
* Injectable for hermetic tests.
*/
export function fetchSources(env: NodeJS.ProcessEnv = process.env): GbrainSourceRow[] {
const raw = execGbrainJson(["sources", "list", "--json"], { baseEnv: env });
if (raw === null) throw new Error("gbrain sources list returned no JSON");
return parseSourcesList(raw);
}
export interface RemoveDecision {
allow: boolean;
/** Extra args to append to `sources remove` (e.g. --keep-storage). */
extraArgs: string[];
reason: string;
}
/**
* Decide whether `sources remove <id>` is safe, and with what flags.
*
* Fail-closed cases (allow=false):
* - sources list unreadable/unparseable (can't prove the row is safe).
* - the row is user-managed (remote_url set AND local_path outside gbrain's
* clones) and gbrain has no --keep-storage to protect the files.
*View on GitHub (pinned to 94993f7401)
Solutions
- Run `gbrain sources list --json` manually in the same environment and inspect stdout — if no JSON appears, upgrade gbrain to a version that supports --json.
- Ensure no wrapper/pipe is stripping stdout before the Node process reads it.
- Confirm gbrain is the intended binary on PATH (which gbrain) and not a stub.
- Run /setup-gbrain to (re)configure the gbrain installation.
Defensive patterns
Strategy: try-catch
Validate before calling
import { execFileSync } from 'child_process';
function gbrainEmitsJson(env: NodeJS.ProcessEnv = process.env): boolean {
try {
const out = execFileSync('gbrain', ['sources', 'list', '--json'], { encoding: 'utf-8', env, timeout: 5_000 });
return out.trim().startsWith('[') || out.trim().startsWith('{');
} catch { return false; }
} Try / catch
try {
return fetchSources(env);
} catch (e) {
if (e instanceof Error && e.message === 'gbrain sources list returned no JSON') {
// Treat as fail-closed: abort the destructive op, do not assume empty list
throw new Error('Cannot verify gbrain sources; aborting destructive operation. Run /setup-gbrain.');
}
throw e;
} Prevention
- Run /setup-gbrain and confirm `gbrain sources list --json` prints JSON before invoking guards.
- Pin a gbrain version known to support --json.
- Ensure no wrapper swallows gbrain's stdout.
- Treat this error as fail-closed in destructive-op flows.
When it happens
Trigger: execGbrainJson runs gbrain sources list --json; the command exits but stdout contains no JSON token (e.g. only log output, a help banner, or empty stdout from a misconfigured subcommand). raw === null triggers the throw.
Common situations: gbrain version where --json is not yet supported and prints human text; GSTDOUT redirected/captured incorrectly; gbrain printed an error to stdout instead of stderr and exited 0; a wrapper script swallowed the JSON stream.
Related errors
- gbrain sources list returned non-JSON output: ${(err as Erro
- Unknown host '${arg}'. Valid hosts: ${ALL_HOST_NAMES.join(',
- invalid_allowlist
- gbrain CLI not on PATH
- gbrain not configured (run /setup-gbrain)
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/959a04f14616dd7c.
Report an issue: GitHub.