{"record":{"id":"86790974755e2650","repo":"garrytan/gstack","slug":"gbrain-sources-add-id-failed-add-stderr-a","errorCode":null,"errorMessage":"gbrain sources add ${id} failed: ${add.stderr || add.stdout || `exit ${add.status}`}","messagePattern":"gbrain sources add (.+?) failed: (.+?)`\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/gbrain-sources.ts","lineNumber":252,"sourceCode":"          shell: NEEDS_SHELL_ON_WINDOWS, // #1731: gbrain is a .cmd shim on Windows\n        },\n      );\n      if (rm.status !== 0) {\n        throw new Error(`gbrain sources remove ${id} failed: ${rm.stderr || rm.stdout || `exit ${rm.status}`}`);\n      }\n    }\n\n    // Add.\n    const addArgs = [\"sources\", \"add\", id, \"--path\", path];\n    if (federated) addArgs.push(\"--federated\");\n    const add = spawnSync(\"gbrain\", addArgs, {\n      encoding: \"utf-8\",\n      timeout: 30_000,\n      env,\n      shell: NEEDS_SHELL_ON_WINDOWS, // #1731: gbrain is a .cmd shim on Windows\n    });\n    if (add.status !== 0) {\n      throw new Error(`gbrain sources add ${id} failed: ${add.stderr || add.stdout || `exit ${add.status}`}`);\n    }\n\n    return {\n      changed: true,\n      state: { status: \"match\", registered_path: path },\n    };\n  }, \"gbrain-sources\");\n}\n\n/**\n * Get page_count for a registered source. Returns null if source is absent or if\n * page_count is missing/invalid in the JSON. Used by the verdict block + preamble\n * variant selection.\n */\nexport function sourcePageCount(id: string, env?: NodeJS.ProcessEnv): number | null {\n  let stdout: string;\n  try {\n    stdout = execFileSync(\"gbrain\", [\"sources\", \"list\", \"--json\"], {","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/lib/gbrain-sources.ts#L234-L270","documentation":"Thrown after spawnSync('gbrain', ['sources','add',id,'--path',path,(--federated)]) returns non-zero at the end of drift re-registration (or a fresh registration). Like 321 it surfaces the child's stderr/stdout/exit code. The remove already succeeded, so a failure here leaves the source unregistered.","triggerScenarios":"The add spawnSync in ensureSourceRegistered() exits non-zero. Typical causes: path does not exist or is unreadable, the id already exists (race with another sync), --federated rejected by a non-federated gbrain, gbrain not on PATH, or a 30s timeout.","commonSituations":"Drift sync where the new path is a typo or not yet created; two /sync-gbrain runs racing on the same source; gbrain version that rejects --federated; Windows .cmd shim resolution failure; the path is a symlink whose target is gone.","solutions":["Run `gbrain sources add <id> --path <path>` manually to see gbrain's own error.","Confirm the path exists and is readable by the gbrain process before syncing.","Ensure no concurrent sync is running (check for a gbrain lock file).","Verify gbrain is on PATH and >= the version that supports your flags."],"exampleFix":"// before\nconst add = spawnSync('gbrain', addArgs, { encoding:'utf-8', timeout:30_000, env, shell: NEEDS_SHELL_ON_WINDOWS });\nif (add.status !== 0) throw new Error(`gbrain sources add ${id} failed: ${add.stderr || add.stdout || `exit ${add.status}`}`);\n\n// after: validate path first, distinguish spawn/signal/exit\nif (!fs.existsSync(path)) throw new Error(`cannot add ${id}: path does not exist: ${path}`);\nconst add = spawnSync('gbrain', addArgs, {...});\nif (add.error) throw new Error(`gbrain add ${id} spawn failed: ${add.error.message}`);\nif (add.signal) throw new Error(`gbrain add ${id} killed by ${add.signal}`);\nif (add.status !== 0) throw new Error(`gbrain sources add ${id} failed: ${add.stderr || add.stdout || `exit ${add.status}`}`);","handlingStrategy":"validation","validationCode":"// Validate the path exists and gbrain is callable before triggering an add.\nimport fs from 'node:fs';\nimport { spawnSync } from 'node:child_process';\n\nfunction preflightAdd(id: string, p: string, env = process.env): string | null {\n  if (!fs.existsSync(p)) return `path does not exist: ${p}`;\n  const v = spawnSync('gbrain', ['--version'], { encoding: 'utf-8', env, shell: process.platform === 'win32' });\n  if (v.status !== 0) return 'gbrain not on PATH or not executable';\n  return null;\n}\n\nconst err = preflightAdd(id, path);\nif (err) throw new Error(`preflight: ${err}`);","typeGuard":null,"tryCatchPattern":"try {\n  await ensureSourceRegistered(id, path);\n} catch (e) {\n  if (/already registered|exists/i.test(String(e?.message ?? ''))) {\n    // idempotent — source is registered, treat as success\n    return;\n  }\n  throw e;\n}","preventionTips":["Create the source directory before referencing it in a sync.","Avoid concurrent /sync-gbrain runs on the same id (check for a lock).","Confirm federated support matches your gbrain build before passing --federated.","On Windows, ensure the .cmd shim path is correct."],"tags":["gbrain","subprocess","spawnsync","registration","windows"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}