{"record":{"id":"6c3266b6ed32632b","repo":"sinelaw/fresh","slug":"ag-exited-with-code-r-exit-code-r-stderr","errorCode":null,"errorMessage":"ag exited with code ${r.exit_code}: ${r.stderr}","messagePattern":"ag exited with code (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/plugins/live_grep.ts","lineNumber":506,"sourceCode":"    const args = [\n      \"--column\",\n      \"--numbers\",\n      \"--nogroup\",\n      \"--nocolor\",\n      \"--smart-case\",\n      \"--ignore\", \".git\",\n      \"--ignore\", \"node_modules\",\n      \"--ignore\", \"target\",\n      \"--ignore\", \"*.lock\",\n    ];\n    if (regex === false) args.push(\"--literal\");\n    if (wholeWord) args.push(\"--word-regexp\");\n    args.push(\"--\", query);\n    const r = await editor.spawnProcess(\"ag\", args, cwd);\n    if (r.exit_code === 0 || r.exit_code === 1) {\n      return parseGrepOutput(r.stdout, maxResults, (msg) => editor.debug(msg)) as GrepMatch[];\n    }\n    throw new Error(`ag exited with code ${r.exit_code}: ${r.stderr}`);\n  },\n});\n\n/** The cwd git-grep should run in: the caller's `preferred` cwd when it is\n *  itself inside a repo, else the active buffer's dir (monorepo: the workspace\n *  root isn't a repo but the open file is). Returns null when neither is a\n *  repo. Used by both `isAvailable` and `search` so they can't disagree. */\nasync function gitGrepCwd(preferred: string): Promise<string | null> {\n  const inRepo = await editor.spawnProcess(\n    \"git\", [\"rev-parse\", \"--is-inside-work-tree\"], preferred\n  );\n  if (inRepo.exit_code === 0) return preferred;\n  const cand = gitCwdCandidate(editor);\n  if (cand !== preferred) {\n    const r = await editor.spawnProcess(\n      \"git\", [\"rev-parse\", \"--is-inside-work-tree\"], cand\n    );\n    if (r.exit_code === 0) return cand;","sourceCodeStart":488,"sourceCodeEnd":524,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/plugins/live_grep.ts#L488-L524","documentation":"The live_grep plugin's `ag` (the_silver_searcher) provider throws this when the spawned ag process exits with a code other than 0 or 1 (1 means 'no matches' and is handled as an empty result). Codes like 2 indicate ag itself failed — typically a bad regex, bad flags, or an unusable search directory. The stderr text appended to the message carries ag's own diagnostic.","triggerScenarios":"Invoking the live-grep ag provider when: (1) the query is an invalid regex in regex mode (ag exits 2), (2) an unsupported flag combination is passed (wholeWord/wholePath variants on unusual ag versions), or (3) the cwd is missing or unreadable.","commonSituations":"Ag not installed properly or an old version lacking flags like --word-regexp/--literal; typing malformed regex like '(' with regex mode on; searching from a deleted or unreadable working directory.","solutions":["Fix the query pattern if regex mode is enabled (validate or escape it).","Run `ag <pattern>` by hand in the same cwd and read stderr for the underlying cause.","Confirm ag is installed and reasonably current (`ag --version`); upgrade if flags are unsupported.","Prefer the ripgrep provider (higher priority) if ag keeps failing."],"exampleFix":"// before\nconst r = await editor.spawnProcess(\"ag\", args, cwd);\n// after\n// validate the regex before spawning when regex mode is on\nif (regex) { try { new RegExp(query); } catch (e) { throw new Error(`invalid pattern: ${query}`); } }\nconst r = await editor.spawnProcess(\"ag\", args, cwd);","handlingStrategy":"try-catch","validationCode":"if (regexMode) { try { new RegExp(query); } catch { throw new Error(`invalid regex: ${query}`); } }\nawait editor.spawnProcess(\"ag\", [\"--version\"], cwd); // backend present check","typeGuard":"function isSpawnResult(r) { return r && typeof r.exit_code === \"number\" && typeof r.stderr === \"string\"; }","tryCatchPattern":"try {\n  results = await agProvider.search(query, opts);\n} catch (e) {\n  if (/ag exited with code/.test(String(e))) {\n    results = await rgProvider.search(query, opts); // fall back to another backend\n  } else { throw e; }\n}","preventionTips":["Verify `ag --version` works in the target environment before relying on this provider.","Escape or validate user regex input in regex mode.","Confirm the search cwd exists and is readable.","Keep a higher-priority provider (ripgrep) installed so ag is not required."],"tags":["silver-searcher","external-process","search","exit-code"],"backgroundTag":"git-command-failed","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}