summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-11-03 23:35:15 -0500
committerDax Raad <[email protected]>2025-11-03 23:36:04 -0500
commitc103052f937362c6a01aee896c0e37e886f8f467 (patch)
treea91ca285a1107b1aed6906659bca297b23a8bbac
parent68039d4c71934c75381fe4f7708760cad7d8b569 (diff)
downloadopencode-c103052f937362c6a01aee896c0e37e886f8f467.tar.gz
opencode-c103052f937362c6a01aee896c0e37e886f8f467.zip
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
-rw-r--r--packages/opencode/src/cli/cmd/tui/routes/session/index.tsx131
1 files changed, 70 insertions, 61 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
index e114f1383..cd41c98a4 100644
--- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
+++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
@@ -480,22 +480,26 @@ export function Session() {
const diffText = s.revert?.diff || ""
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 {
@@ -1245,58 +1249,63 @@ ToolRegistry.register<typeof EditTool>({
const diff = createMemo(() => {
const diff = props.metadata.diff ?? props.permission["diff"]
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
}
})