{"record":{"id":"97f64e517a5d1f96","repo":"vxcontrol/pentagi","slug":"internal-error-building-patch-w","errorCode":null,"errorMessage":"internal error building patch: %w","messagePattern":"internal error building patch: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/file_diff.go","lineNumber":303,"sourceCode":"// and the number of hunks applied, or a descriptive error naming every hunk\n// that failed to apply and a preview of the content it looked for -\n// content is returned unchanged (empty) on error, so a partial/bad diff\n// never corrupts the file. Exported so other packages (e.g. the provider\n// tester) can exercise the exact production diff-merge semantics without\n// going through EditFile's Docker-backed read/write.\nfunc ApplyUnifiedDiff(content, diffText string) (string, int, error) {\n\thunks, err := parseUnifiedDiff(diffText)\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\thunks = ensureContextBoundaries(hunks, content)\n\n\tpatchText := buildGoDiffPatchText(hunks, content)\n\n\tdmp := diffmatchpatch.New()\n\tpatches, err := dmp.PatchFromText(patchText)\n\tif err != nil {\n\t\treturn \"\", 0, fmt.Errorf(\"internal error building patch: %w\", err)\n\t}\n\n\tnewContent, applied := dmp.PatchApply(patches, content)\n\n\tvar failed []string\n\tfor i, ok := range applied {\n\t\tif !ok && i < len(hunks) {\n\t\t\tfailed = append(failed, fmt.Sprintf(\"%s (not found in the file, looked for: %q)\", hunks[i].header, hunkOldPreview(hunks[i])))\n\t\t}\n\t}\n\tif len(failed) > 0 {\n\t\treturn \"\", 0, fmt.Errorf(\n\t\t\t\"%d of %d hunk(s) could not be applied - read the file again and retry with context that matches its current content exactly:\\n%s\",\n\t\t\tlen(failed), len(hunks), strings.Join(failed, \"\\n\"),\n\t\t)\n\t}\n\n\treturn newContent, len(hunks), nil","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/file_diff.go#L285-L321","documentation":"After parsing the hunks, ApplyUnifiedDiff re-serializes them via buildGoDiffPatchText and feeds the result to diff-match-patch's PatchFromText. If dmp cannot parse the internally generated patch text, this is treated as a bug in the tool itself ('internal error'), not bad user input — the parsed hunks were valid but the reconstruction produced text diff-match-patch rejects.","triggerScenarios":"buildGoDiffPatchText emits characters or encodings diff-match-patch's strict PatchFromText grammar rejects — typically due to unusual content in hunk lines (control characters, malformed URL-encoded indices, edge cases around empty lines or very large offsets).","commonSituations":"Diffing binary-ish or non-UTF8 file content; hunk text containing raw control bytes; rare dmp library version incompatibilities with the generated patch format; files with mixed line endings confusing the reconstruction.","solutions":["This is an internal invariant failure — report it with the diff input and target file that triggered it.","Retry the edit with a smaller, simpler diff (fewer hunks, ASCII-only context) as a workaround.","Sanitize the target file to clean UTF-8 without control characters, then re-read it and re-apply.","Check the diff-match-patch dependency version for known PatchFromText regressions in go.mod."],"exampleFix":"// workaround: split one large multi-hunk edit into smaller sequential edits\n// before: ApplyUnifiedDiff(ctx, path, bigDiffWith12Hunks)\n// after:  ApplyUnifiedDiff(ctx, path, hunk1Diff); ApplyUnifiedDiff(ctx, path, hunk2Diff)","handlingStrategy":"fallback","validationCode":"func sanitizeForDiff(s string) string {\n\treturn strings.Map(func(r rune) rune {\n\t\tif r == '\\n' || r == '\\t' || (r >= 0x20 && r != 0x7f) { return r }\n\t\treturn -1 // drop control chars dmp may choke on\n\t}, s)\n}","typeGuard":null,"tryCatchPattern":"newContent, n, err := ApplyUnifiedDiff(ctx, path, diff)\nif err != nil && strings.Contains(err.Error(), \"internal error building patch\") {\n\t// fallback: rewrite file via read-modify-write instead of patching\n\treturn rewriteFileManually(ctx, path, hunks)\n}","preventionTips":["Keep target files clean UTF-8 without control characters","Prefer many small diffs over one huge multi-hunk diff","Track diff-match-patch dependency versions for PatchFromText regressions","Report internal-error occurrences upstream — they indicate a tool bug, not user error"],"tags":["diff","diff-match-patch","internal-error","patch"],"backgroundTag":"patch-parse-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}