From 424f6f9ca7fe17798d88d68e6e9cbac344894819 Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Mon, 12 Jan 2026 15:19:14 -0800 Subject: [PATCH] fix: handle broken symlinks gracefully in grep tool --- packages/opencode/src/tool/grep.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index ad62621e072..097dedf4aaf 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -37,7 +37,15 @@ export const GrepTool = Tool.define("grep", { await assertExternalDirectory(ctx, searchPath, { kind: "directory" }) const rgPath = await Ripgrep.filepath() - const args = ["-nH", "--hidden", "--follow", "--field-match-separator=|", "--regexp", params.pattern] + const args = [ + "-nH", + "--hidden", + "--follow", + "--no-messages", + "--field-match-separator=|", + "--regexp", + params.pattern, + ] if (params.include) { args.push("--glob", params.include) } @@ -52,7 +60,10 @@ export const GrepTool = Tool.define("grep", { const errorOutput = await new Response(proc.stderr).text() const exitCode = await proc.exited - if (exitCode === 1) { + // Exit codes: 0 = matches found, 1 = no matches, 2 = errors (but may still have matches) + // With --no-messages, we suppress error output but still get exit code 2 for broken symlinks etc. + // Only fail if exit code is 2 AND no output was produced + if (exitCode === 1 || (exitCode === 2 && !output.trim())) { return { title: params.pattern, metadata: { matches: 0, truncated: false }, @@ -60,10 +71,12 @@ export const GrepTool = Tool.define("grep", { } } - if (exitCode !== 0) { + if (exitCode !== 0 && exitCode !== 2) { throw new Error(`ripgrep failed: ${errorOutput}`) } + const hasErrors = exitCode === 2 + // Handle both Unix (\n) and Windows (\r\n) line endings const lines = output.trim().split(/\r?\n/) const matches = [] @@ -124,6 +137,11 @@ export const GrepTool = Tool.define("grep", { outputLines.push("(Results are truncated. Consider using a more specific path or pattern.)") } + if (hasErrors) { + outputLines.push("") + outputLines.push("(Some paths were inaccessible and skipped)") + } + return { title: params.pattern, metadata: {