Browse Source

fix: handle parsePatch errors in TUI to prevent crashes

Wrap parsePatch calls in try-catch blocks to gracefully handle malformed
diffs that can occur when undoing after tool_use/tool_result errors or
cancelled prompts. Prevents TUI from crashing with 'Added line count did not
match for hunk' error.

Fixes #3700
Dax Raad 3 months ago
parent
commit
c103052f93
1 changed files with 70 additions and 61 deletions
  1. 70 61
      packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

+ 70 - 61
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

@@ -480,22 +480,26 @@ export function Session() {
       const diffText = s.revert?.diff || ""
       const diffText = s.revert?.diff || ""
       if (!diffText) return []
       if (!diffText) return []
 
 
-      const patches = parsePatch(diffText)
-      return patches.map((patch) => {
-        const filename = patch.newFileName || patch.oldFileName || "unknown"
-        const cleanFilename = filename.replace(/^[ab]\//, "")
-        return {
-          filename: cleanFilename,
-          additions: patch.hunks.reduce(
-            (sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("+")).length,
-            0,
-          ),
-          deletions: patch.hunks.reduce(
-            (sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("-")).length,
-            0,
-          ),
-        }
-      })
+      try {
+        const patches = parsePatch(diffText)
+        return patches.map((patch) => {
+          const filename = patch.newFileName || patch.oldFileName || "unknown"
+          const cleanFilename = filename.replace(/^[ab]\//, "")
+          return {
+            filename: cleanFilename,
+            additions: patch.hunks.reduce(
+              (sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("+")).length,
+              0,
+            ),
+            deletions: patch.hunks.reduce(
+              (sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("-")).length,
+              0,
+            ),
+          }
+        })
+      } catch (error) {
+        return []
+      }
     })()
     })()
 
 
     return {
     return {
@@ -1245,58 +1249,63 @@ ToolRegistry.register<typeof EditTool>({
     const diff = createMemo(() => {
     const diff = createMemo(() => {
       const diff = props.metadata.diff ?? props.permission["diff"]
       const diff = props.metadata.diff ?? props.permission["diff"]
       if (!diff) return null
       if (!diff) return null
-      const patches = parsePatch(diff)
-      if (patches.length === 0) return null
-
-      const patch = patches[0]
-      const oldLines: string[] = []
-      const newLines: string[] = []
-
-      for (const hunk of patch.hunks) {
-        let i = 0
-        while (i < hunk.lines.length) {
-          const line = hunk.lines[i]
-
-          if (line.startsWith("-")) {
-            const removedLines: string[] = []
-            while (i < hunk.lines.length && hunk.lines[i].startsWith("-")) {
-              removedLines.push("- " + hunk.lines[i].slice(1))
-              i++
-            }
 
 
-            const addedLines: string[] = []
-            while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
-              addedLines.push("+ " + hunk.lines[i].slice(1))
-              i++
-            }
+      try {
+        const patches = parsePatch(diff)
+        if (patches.length === 0) return null
 
 
-            const maxLen = Math.max(removedLines.length, addedLines.length)
-            for (let j = 0; j < maxLen; j++) {
-              oldLines.push(removedLines[j] ?? "")
-              newLines.push(addedLines[j] ?? "")
-            }
-          } else if (line.startsWith("+")) {
-            const addedLines: string[] = []
-            while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
-              addedLines.push("+ " + hunk.lines[i].slice(1))
-              i++
-            }
+        const patch = patches[0]
+        const oldLines: string[] = []
+        const newLines: string[] = []
+
+        for (const hunk of patch.hunks) {
+          let i = 0
+          while (i < hunk.lines.length) {
+            const line = hunk.lines[i]
+
+            if (line.startsWith("-")) {
+              const removedLines: string[] = []
+              while (i < hunk.lines.length && hunk.lines[i].startsWith("-")) {
+                removedLines.push("- " + hunk.lines[i].slice(1))
+                i++
+              }
+
+              const addedLines: string[] = []
+              while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
+                addedLines.push("+ " + hunk.lines[i].slice(1))
+                i++
+              }
 
 
-            for (const added of addedLines) {
-              oldLines.push("")
-              newLines.push(added)
+              const maxLen = Math.max(removedLines.length, addedLines.length)
+              for (let j = 0; j < maxLen; j++) {
+                oldLines.push(removedLines[j] ?? "")
+                newLines.push(addedLines[j] ?? "")
+              }
+            } else if (line.startsWith("+")) {
+              const addedLines: string[] = []
+              while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
+                addedLines.push("+ " + hunk.lines[i].slice(1))
+                i++
+              }
+
+              for (const added of addedLines) {
+                oldLines.push("")
+                newLines.push(added)
+              }
+            } else {
+              oldLines.push("  " + line.slice(1))
+              newLines.push("  " + line.slice(1))
+              i++
             }
             }
-          } else {
-            oldLines.push("  " + line.slice(1))
-            newLines.push("  " + line.slice(1))
-            i++
           }
           }
         }
         }
-      }
 
 
-      return {
-        oldContent: oldLines.join("\n"),
-        newContent: newLines.join("\n"),
+        return {
+          oldContent: oldLines.join("\n"),
+          newContent: newLines.join("\n"),
+        }
+      } catch (error) {
+        return null
       }
       }
     })
     })