{"record":{"id":"9679eb05025c6cd5","repo":"sinelaw/fresh","slug":"no-search-backend-available-install-ripgrep-or-register-a","errorCode":null,"errorMessage":"no search backend available — install ripgrep, or register a provider via init.ts (`editor.getPluginApi(\"live-grep\")?.registerProvider(...)`).","messagePattern":"no search backend available — install ripgrep, or register a provider via init\\.ts \\(`editor\\.getPluginApi\\(\"live-grep\"\\)\\?\\.registerProvider\\(\\.\\.\\.\\)`\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/fresh-editor/plugins/live_grep.ts","lineNumber":991,"sourceCode":"    throw new Error(`${provider.name}: ${e instanceof Error ? e.message : String(e)}`);\n  }\n}\n\n// Fan the query out across every enabled scope and merge into one\n// capped, tagged result list. Order is files → buffers → diagnostics\n// so the most common hits lead.\nasync function search(query: string): Promise<GrepMatch[]> {\n  lastQuery = query;\n  const wasTruncated = lastSearchTruncated;\n  const results: GrepMatch[] = [];\n  const remaining = () => MAX_RESULTS - results.length;\n\n  if (scopeEnabled.files || scopeEnabled.ignored) {\n    const fileMatches = await searchFiles(query);\n    if (fileMatches === null) {\n      // No grep backend. Only fatal if there's nothing else to search.\n      if (!scopeEnabled.buffers && !scopeEnabled.diagnostics) {\n        throw new Error(\n          \"no search backend available — install ripgrep, or register a provider via init.ts (`editor.getPluginApi(\\\"live-grep\\\")?.registerProvider(...)`).\"\n        );\n      }\n    } else {\n      for (const m of fileMatches) {\n        if (results.length >= MAX_RESULTS) break;\n        results.push(m);\n      }\n    }\n  }\n\n  if (scopeEnabled.buffers && remaining() > 0) {\n    const filesActive = scopeEnabled.files || scopeEnabled.ignored;\n    results.push(...await searchOpenBuffers(query, remaining(), !filesActive));\n  }\n\n  if (scopeEnabled.terminals && remaining() > 0) {\n    results.push(...await searchTerminals(query, remaining()));","sourceCodeStart":973,"sourceCodeEnd":1009,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/plugins/live_grep.ts#L973-L1009","documentation":"The live_grep fan-out search throws this when the file scope is enabled but searchFiles returns null — meaning no grep backend (ripgrep, ag, git grep, ack, grep, or a registered provider) could be found at all — AND the buffers and diagnostics scopes are disabled, so there is literally nothing to search. The plugin treats a missing backend as tolerable only when other scopes can still produce results.","triggerScenarios":"Running a live-grep search with the files (or ignored) scope enabled on a machine where none of rg/ag/git/ack/grep binaries are locatable by spawnProcess, while both the buffers and diagnostics scopes are turned off in scopeEnabled.","commonSituations":"Fresh containers/CI images or minimal Windows/macOS installs with no grep-family tool in PATH; users disabling all scopes except files after uninstalling ripgrep; wrong PATH inside the editor's spawn environment so binaries aren't found even though installed.","solutions":["Install ripgrep (`brew install ripgrep`, `apt install ripgrep`, `winget install BurntSushi.ripgrep`) — the primary backend.","Or register a custom provider at startup via `editor.getPluginApi(\"live-grep\")?.registerProvider(...)` in init.ts, as the message instructs.","Enable the buffers and/or diagnostics scopes so a missing file backend is not fatal.","Check that the editor's spawn environment PATH includes the directory containing your grep tools."],"exampleFix":"// before (init.ts) — nothing registered, no ripgrep installed\n// after\neditor.getPluginApi(\"live-grep\")?.registerProvider({\n  name: \"custom-grep\",\n  priority: 0,\n  search: async (query, opts) => {\n    /* call your own search binary */\n    return [];\n  },\n});","handlingStrategy":"fallback","validationCode":"async function hasGrepBackend(editor, cwd) {\n  for (const b of [\"rg\", \"ag\", \"git\", \"ack\", \"grep\"]) {\n    try { await editor.spawnProcess(b, [\"--version\"], cwd); return true; } catch {}\n  }\n  return false;\n}\n// before searching with only the files scope: if (!(await hasGrepBackend(editor, cwd))) install ripgrep or enable other scopes;","typeGuard":"null","tryCatchPattern":"try {\n  await liveGrep.search(query);\n} catch (e) {\n  if (/no search backend available/.test(String(e))) {\n    // degrade: retry with buffers scope enabled, or prompt the user to install ripgrep\n    await liveGrep.search(query, { scopes: { files: false, buffers: true, diagnostics: true } });\n  } else { throw e; }\n}","preventionTips":["Install ripgrep in every environment where the editor runs (containers, CI, minimal images).","Keep at least one non-file scope (buffers/diagnostics) enabled as a safety net.","Register a custom provider in init.ts for restricted environments without grep tools.","Verify PATH inside the editor's spawn environment includes tool directories."],"tags":["missing-dependency","ripgrep","search","environment"],"backgroundTag":"missing-dependency","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"}