sinelaw/fresh · critical

no search backend available — install ripgrep, or register…

Error message

no search backend available — install ripgrep, or register a provider via init.ts (`editor.getPluginApi("live-grep")?.registerProvider(...)`).

What it means

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.

Solutions

  1. Install ripgrep (`brew install ripgrep`, `apt install ripgrep`, `winget install BurntSushi.ripgrep`) — the primary backend.
  2. Or register a custom provider at startup via `editor.getPluginApi("live-grep")?.registerProvider(...)` in init.ts, as the message instructs.
  3. Enable the buffers and/or diagnostics scopes so a missing file backend is not fatal.
  4. Check that the editor's spawn environment PATH includes the directory containing your grep tools.

Example fix

// before (init.ts) — nothing registered, no ripgrep installed
// after
editor.getPluginApi("live-grep")?.registerProvider({
  name: "custom-grep",
  priority: 0,
  search: async (query, opts) => {
    /* call your own search binary */
    return [];
  },
});
Defensive patterns

Strategy: fallback

Validate before calling

async function hasGrepBackend(editor, cwd) {
  for (const b of ["rg", "ag", "git", "ack", "grep"]) {
    try { await editor.spawnProcess(b, ["--version"], cwd); return true; } catch {}
  }
  return false;
}
// before searching with only the files scope: if (!(await hasGrepBackend(editor, cwd))) install ripgrep or enable other scopes;

Type guard

null

Try / catch

try {
  await liveGrep.search(query);
} catch (e) {
  if (/no search backend available/.test(String(e))) {
    // degrade: retry with buffers scope enabled, or prompt the user to install ripgrep
    await liveGrep.search(query, { scopes: { files: false, buffers: true, diagnostics: true } });
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/9679eb05025c6cd5. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/plugins/live_grep.ts:991

    throw new Error(`${provider.name}: ${e instanceof Error ? e.message : String(e)}`);
  }
}

// Fan the query out across every enabled scope and merge into one
// capped, tagged result list. Order is files → buffers → diagnostics
// so the most common hits lead.
async function search(query: string): Promise<GrepMatch[]> {
  lastQuery = query;
  const wasTruncated = lastSearchTruncated;
  const results: GrepMatch[] = [];
  const remaining = () => MAX_RESULTS - results.length;

  if (scopeEnabled.files || scopeEnabled.ignored) {
    const fileMatches = await searchFiles(query);
    if (fileMatches === null) {
      // No grep backend. Only fatal if there's nothing else to search.
      if (!scopeEnabled.buffers && !scopeEnabled.diagnostics) {
        throw new Error(
          "no search backend available — install ripgrep, or register a provider via init.ts (`editor.getPluginApi(\"live-grep\")?.registerProvider(...)`)."
        );
      }
    } else {
      for (const m of fileMatches) {
        if (results.length >= MAX_RESULTS) break;
        results.push(m);
      }
    }
  }

  if (scopeEnabled.buffers && remaining() > 0) {
    const filesActive = scopeEnabled.files || scopeEnabled.ignored;
    results.push(...await searchOpenBuffers(query, remaining(), !filesActive));
  }

  if (scopeEnabled.terminals && remaining() > 0) {
    results.push(...await searchTerminals(query, remaining()));

View on GitHub (pinned to 67894ca546)