sinelaw/fresh · error

${provider.name}: ${e instanceof Error ? e.message …

Error message

${provider.name}: ${e instanceof Error ? e.message : String(e)}

What it means

In live_grep's searchFiles wrapper, when an individual provider (rg, ag, git grep, ack, grep, or a user-registered one) throws, the error is logged via editor.error and re-thrown wrapped as "<providerName>: <original message>". This preserves which backend failed while propagating the underlying cause (typically one of the 'X exited with code N' errors or a spawn failure like 'binary not found').

Solutions

  1. Read the wrapped provider name to identify which backend failed, then apply that backend's specific fix (see the per-provider 'exited with code' errors).
  2. Catch this error at the call site and fall back to the buffers/diagnostics scopes when file search is unavailable.
  3. Install ripgrep so the primary provider succeeds.
  4. If a custom provider is at fault, fix or remove its registration from init.ts.

Example fix

// before
const results = await searchFiles(query);
// after
let results;
try {
  results = await searchFiles(query);
} catch (e) {
  editor.error(`file search unavailable: ${e}`);
  results = null; // continue with buffers/diagnostics scopes
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check at least one backend exists before searching
const backends = ["rg", "ag", "git", "ack", "grep"];
for (const b of backends) { try { await editor.spawnProcess(b, ["--version"], cwd); break; } catch {} }

Type guard

function isProviderError(e: unknown): e is Error { return e instanceof Error && /^\w+: /.test(e.message); } // 'providerName: cause' shape

Try / catch

try {
  const files = await searchFiles(query);
} catch (e) {
  const provider = String(e).split(":")[0];
  editor.error(`backend ${provider} failed — degrading to buffers/diagnostics scopes`);
  files = null;
}

Prevention

When it happens

Trigger: Any live-grep file-scope search in which the selected/first available provider throws — e.g. rg exiting non-zero, a missing backend binary, or a registered provider raising its own error during the search call.

Common situations: Debugging why live-grep shows no results: the wrapped message names the failing backend; users on machines without ripgrep hitting a spawn failure; user-registered custom providers (via the plugin API) throwing their own errors.

Related errors


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

Appendix: source

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

// Run the project-file grep for the enabled file-backed scopes
// (`files` / `ignored`). Returns null when no provider is available so
// the caller can decide whether that's fatal (no other scope on) or
// merely a skipped source.
async function searchFiles(query: string): Promise<GrepMatch[] | null> {
  const provider = await selectProvider();
  if (!provider) return null;
  try {
    const results = await provider.search(query, {
      cwd: editor.getCwd(),
      maxResults: MAX_RESULTS,
      includeIgnored: scopeEnabled.ignored,
      wholeWord: searchModes.word,
      regex: searchModes.regex,
    });
    return results.map((m) => ({ ...m, source: "files" as const }));
  } catch (e) {
    editor.error(`[live_grep:${provider.name}] ${e}`);
    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(

View on GitHub (pinned to 67894ca546)