{"record":{"id":"016c2941e9a49d19","repo":"sinelaw/fresh","slug":"provider-name-e-instanceof-error-e-message-string-e","errorCode":null,"errorMessage":"${provider.name}: ${e instanceof Error ? e.message : String(e)}","messagePattern":"\\$\\{provider\\.name\\}: \\$\\{e instanceof Error \\? e\\.message : String\\(e\\)\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/plugins/live_grep.ts","lineNumber":973,"sourceCode":"// Run the project-file grep for the enabled file-backed scopes\n// (`files` / `ignored`). Returns null when no provider is available so\n// the caller can decide whether that's fatal (no other scope on) or\n// merely a skipped source.\nasync function searchFiles(query: string): Promise<GrepMatch[] | null> {\n  const provider = await selectProvider();\n  if (!provider) return null;\n  try {\n    const results = await provider.search(query, {\n      cwd: editor.getCwd(),\n      maxResults: MAX_RESULTS,\n      includeIgnored: scopeEnabled.ignored,\n      wholeWord: searchModes.word,\n      regex: searchModes.regex,\n    });\n    return results.map((m) => ({ ...m, source: \"files\" as const }));\n  } catch (e) {\n    editor.error(`[live_grep:${provider.name}] ${e}`);\n    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(","sourceCodeStart":955,"sourceCodeEnd":991,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/plugins/live_grep.ts#L955-L991","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Catch this error at the call site and fall back to the buffers/diagnostics scopes when file search is unavailable.","Install ripgrep so the primary provider succeeds.","If a custom provider is at fault, fix or remove its registration from init.ts."],"exampleFix":"// before\nconst results = await searchFiles(query);\n// after\nlet results;\ntry {\n  results = await searchFiles(query);\n} catch (e) {\n  editor.error(`file search unavailable: ${e}`);\n  results = null; // continue with buffers/diagnostics scopes\n}","handlingStrategy":"try-catch","validationCode":"// check at least one backend exists before searching\nconst backends = [\"rg\", \"ag\", \"git\", \"ack\", \"grep\"];\nfor (const b of backends) { try { await editor.spawnProcess(b, [\"--version\"], cwd); break; } catch {} }","typeGuard":"function isProviderError(e: unknown): e is Error { return e instanceof Error && /^\\w+: /.test(e.message); } // 'providerName: cause' shape","tryCatchPattern":"try {\n  const files = await searchFiles(query);\n} catch (e) {\n  const provider = String(e).split(\":\")[0];\n  editor.error(`backend ${provider} failed — degrading to buffers/diagnostics scopes`);\n  files = null;\n}","preventionTips":["Parse the '<provider>: <cause>' prefix to know which backend failed.","Always install ripgrep as the primary backend.","Keep buffers/diagnostics scopes enabled so a file-backend failure is not fatal.","Test custom providers registered via the plugin API in isolation."],"tags":["error-wrapping","external-process","search","provider"],"backgroundTag":"command-not-found","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"}