{"record":{"id":"fa3d3330428fca48","repo":"sinelaw/fresh","slug":"rg-exited-with-code-r-exit-code-r-stderr","errorCode":null,"errorMessage":"rg exited with code ${r.exit_code}: ${r.stderr}","messagePattern":"rg exited with code (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/plugins/live_grep.ts","lineNumber":472,"sourceCode":"      \"-g\", \"!.git\",\n    ];\n    if (regex === false) args.push(\"--fixed-strings\");\n    if (wholeWord) args.push(\"--word-regexp\");\n    if (includeIgnored) {\n      // Search ignored *and* hidden files (dotfiles). `.git` stays\n      // excluded via the glob above.\n      args.push(\"--no-ignore\", \"--hidden\");\n    } else {\n      // Default: respect ignore files, plus prune the usual heavy\n      // build/vendor dirs and lockfiles that bury real hits.\n      args.push(\"-g\", \"!node_modules\", \"-g\", \"!target\", \"-g\", \"!*.lock\");\n    }\n    args.push(\"--\", query);\n    const r = await editor.spawnProcess(\"rg\", args, cwd);\n    if (r.exit_code === 0) {\n      return parseGrepOutput(r.stdout, maxResults, (msg) => editor.debug(msg)) as GrepMatch[];\n    }\n    throw new Error(`rg exited with code ${r.exit_code}: ${r.stderr}`);\n  },\n});\n\nregisterProvider({\n  name: \"ag\",\n  priority: -2,\n  isAvailable: async () => {\n    try {\n      const r = await editor.spawnProcess(\"ag\", [\"--version\"], editor.getCwd());\n      return r.exit_code === 0;\n    } catch {\n      return false;\n    }\n  },\n  search: async (query, { cwd, maxResults, wholeWord, regex }) => {\n    const args = [\n      \"--column\",\n      \"--numbers\",","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/plugins/live_grep.ts#L454-L490","documentation":"The live_grep plugin's `rg` provider shells out to ripgrep via editor.spawnProcess and throws this error whenever rg terminates with a status other than 0. Unlike the ag/git-grep/ack providers, this one only treats exit code 0 as success, so even ripgrep's benign 'no matches found' exit code 1 becomes an error. Non-zero codes also cover real failures such as exit 2 for an invalid regex pattern or unreadable files, with details in stderr.","triggerScenarios":"Calling the live-grep search with the rg provider active when: (1) rg finds no matches (exit 1, not whitelisted here), (2) the query is an invalid regex while regex mode is on (rg exit 2), (3) rg is missing/broken enough to fail, or (4) the cwd is unreadable.","commonSituations":"Typing a regex query like '[' or 'a(' with regex mode enabled; searching a term that genuinely has no matches (because exit 1 is not handled as empty like the other providers); running in a directory rg cannot traverse; an rg version with different flag support causing argument errors.","solutions":["If regex mode is on, fix the query pattern (escape special characters or use literal mode).","If the query is simply a no-match, note this provider throws on exit 1 — fix the provider to accept `r.exit_code === 0 || r.exit_code === 1` like the ag/git-grep providers do.","Run `rg <pattern>` manually in the target cwd and read stderr to see the exact ripgrep failure.","Verify ripgrep is installed and up to date (`rg --version`)."],"exampleFix":"// before\nif (r.exit_code === 0) {\n  return parseGrepOutput(r.stdout, maxResults, (msg) => editor.debug(msg)) as GrepMatch[];\n}\n// after\nif (r.exit_code === 0 || r.exit_code === 1) {\n  // rg exits 1 when there are no matches — treat as empty.\n  return parseGrepOutput(r.stdout, maxResults, (msg) => editor.debug(msg)) as GrepMatch[];\n}","handlingStrategy":"try-catch","validationCode":"if (regexMode) { try { new RegExp(query); } catch { throw new Error(`invalid regex: ${query}`); } }\nif (typeof editor.spawnProcess !== \"function\") throw new Error(\"spawnProcess unavailable\");","typeGuard":"function isGrepResult(r) { return typeof r === \"object\" && r !== null && typeof r.exit_code === \"number\" && typeof r.stdout === \"string\" && typeof r.stderr === \"string\"; }","tryCatchPattern":"try {\n  const matches = await rgProvider.search(query, opts);\n} catch (e) {\n  if (/rg exited with code 2/.test(String(e))) {\n    // invalid pattern — retry with literal mode\n    matches = await rgProvider.search(query, { ...opts, regex: false });\n  } else if (/rg exited with code 1/.test(String(e))) {\n    matches = []; // no matches surfaced as an error by this provider\n  } else {\n    throw e;\n  }\n}","preventionTips":["Validate regex queries before enabling regex mode.","Prefer backends whose no-match exit code (1) is handled; or patch the provider to accept exit 1.","Keep ripgrep installed and in PATH.","Test searches in the target cwd manually before automating."],"tags":["ripgrep","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"}