Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit c103052

Browse files
committed
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 anomalyco#3700
1 parent 68039d4 commit c103052

1 file changed

Lines changed: 70 additions & 61 deletions

File tree

  • packages/opencode/src/cli/cmd/tui/routes/session

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

Lines changed: 70 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -480,22 +480,26 @@ export function Session() {
480480
const diffText = s.revert?.diff || ""
481481
if (!diffText) return []
482482

483-
const patches = parsePatch(diffText)
484-
return patches.map((patch) => {
485-
const filename = patch.newFileName || patch.oldFileName || "unknown"
486-
const cleanFilename = filename.replace(/^[ab]\//, "")
487-
return {
488-
filename: cleanFilename,
489-
additions: patch.hunks.reduce(
490-
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("+")).length,
491-
0,
492-
),
493-
deletions: patch.hunks.reduce(
494-
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("-")).length,
495-
0,
496-
),
497-
}
498-
})
483+
try {
484+
const patches = parsePatch(diffText)
485+
return patches.map((patch) => {
486+
const filename = patch.newFileName || patch.oldFileName || "unknown"
487+
const cleanFilename = filename.replace(/^[ab]\//, "")
488+
return {
489+
filename: cleanFilename,
490+
additions: patch.hunks.reduce(
491+
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("+")).length,
492+
0,
493+
),
494+
deletions: patch.hunks.reduce(
495+
(sum, hunk) => sum + hunk.lines.filter((line) => line.startsWith("-")).length,
496+
0,
497+
),
498+
}
499+
})
500+
} catch (error) {
501+
return []
502+
}
499503
})()
500504

501505
return {
@@ -1245,58 +1249,63 @@ ToolRegistry.register<typeof EditTool>({
12451249
const diff = createMemo(() => {
12461250
const diff = props.metadata.diff ?? props.permission["diff"]
12471251
if (!diff) return null
1248-
const patches = parsePatch(diff)
1249-
if (patches.length === 0) return null
1250-
1251-
const patch = patches[0]
1252-
const oldLines: string[] = []
1253-
const newLines: string[] = []
1254-
1255-
for (const hunk of patch.hunks) {
1256-
let i = 0
1257-
while (i < hunk.lines.length) {
1258-
const line = hunk.lines[i]
1259-
1260-
if (line.startsWith("-")) {
1261-
const removedLines: string[] = []
1262-
while (i < hunk.lines.length && hunk.lines[i].startsWith("-")) {
1263-
removedLines.push("- " + hunk.lines[i].slice(1))
1264-
i++
1265-
}
12661252

1267-
const addedLines: string[] = []
1268-
while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
1269-
addedLines.push("+ " + hunk.lines[i].slice(1))
1270-
i++
1271-
}
1253+
try {
1254+
const patches = parsePatch(diff)
1255+
if (patches.length === 0) return null
12721256

1273-
const maxLen = Math.max(removedLines.length, addedLines.length)
1274-
for (let j = 0; j < maxLen; j++) {
1275-
oldLines.push(removedLines[j] ?? "")
1276-
newLines.push(addedLines[j] ?? "")
1277-
}
1278-
} else if (line.startsWith("+")) {
1279-
const addedLines: string[] = []
1280-
while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
1281-
addedLines.push("+ " + hunk.lines[i].slice(1))
1282-
i++
1283-
}
1257+
const patch = patches[0]
1258+
const oldLines: string[] = []
1259+
const newLines: string[] = []
1260+
1261+
for (const hunk of patch.hunks) {
1262+
let i = 0
1263+
while (i < hunk.lines.length) {
1264+
const line = hunk.lines[i]
1265+
1266+
if (line.startsWith("-")) {
1267+
const removedLines: string[] = []
1268+
while (i < hunk.lines.length && hunk.lines[i].startsWith("-")) {
1269+
removedLines.push("- " + hunk.lines[i].slice(1))
1270+
i++
1271+
}
1272+
1273+
const addedLines: string[] = []
1274+
while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
1275+
addedLines.push("+ " + hunk.lines[i].slice(1))
1276+
i++
1277+
}
12841278

1285-
for (const added of addedLines) {
1286-
oldLines.push("")
1287-
newLines.push(added)
1279+
const maxLen = Math.max(removedLines.length, addedLines.length)
1280+
for (let j = 0; j < maxLen; j++) {
1281+
oldLines.push(removedLines[j] ?? "")
1282+
newLines.push(addedLines[j] ?? "")
1283+
}
1284+
} else if (line.startsWith("+")) {
1285+
const addedLines: string[] = []
1286+
while (i < hunk.lines.length && hunk.lines[i].startsWith("+")) {
1287+
addedLines.push("+ " + hunk.lines[i].slice(1))
1288+
i++
1289+
}
1290+
1291+
for (const added of addedLines) {
1292+
oldLines.push("")
1293+
newLines.push(added)
1294+
}
1295+
} else {
1296+
oldLines.push(" " + line.slice(1))
1297+
newLines.push(" " + line.slice(1))
1298+
i++
12881299
}
1289-
} else {
1290-
oldLines.push(" " + line.slice(1))
1291-
newLines.push(" " + line.slice(1))
1292-
i++
12931300
}
12941301
}
1295-
}
12961302

1297-
return {
1298-
oldContent: oldLines.join("\n"),
1299-
newContent: newLines.join("\n"),
1303+
return {
1304+
oldContent: oldLines.join("\n"),
1305+
newContent: newLines.join("\n"),
1306+
}
1307+
} catch (error) {
1308+
return null
13001309
}
13011310
})
13021311

0 commit comments

Comments
 (0)