{"record":{"id":"3ed7f4bc35f3c43b","repo":"theonedev/onedev","slug":"illegal-escape-in-patch-fromtext-line","errorCode":null,"errorMessage":"Illegal escape in patch_fromText: ${line}","messagePattern":"Illegal escape in patch_fromText: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/util/diff/DiffMatchPatch.java","lineNumber":2346,"sourceCode":"\t\t\twhile (!text.isEmpty()) {\n\t\t\t\ttry {\n\t\t\t\t\tsign = text.getFirst().charAt(0);\n\t\t\t\t} catch (IndexOutOfBoundsException e) {\n\t\t\t\t\t// Blank line? Whatever.\n\t\t\t\t\ttext.removeFirst();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tline = text.getFirst().substring(1);\n\t\t\t\tline = line.replace(\"+\", \"%2B\"); // decode would change all \"+\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t// to \" \"\n\t\t\t\ttry {\n\t\t\t\t\tline = URLDecoder.decode(line, \"UTF-8\");\n\t\t\t\t} catch (UnsupportedEncodingException e) {\n\t\t\t\t\t// Not likely on modern system.\n\t\t\t\t\tthrow new Error(\"This system does not support UTF-8.\", e);\n\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\t// Malformed URI sequence.\n\t\t\t\t\tthrow new IllegalArgumentException(\"Illegal escape in patch_fromText: \" + line,\n\t\t\t\t\t\t\te);\n\t\t\t\t}\n\t\t\t\tif (sign == '-') {\n\t\t\t\t\t// Deletion.\n\t\t\t\t\tpatch.diffs.add(new Diff(Operation.DELETE, line));\n\t\t\t\t} else if (sign == '+') {\n\t\t\t\t\t// Insertion.\n\t\t\t\t\tpatch.diffs.add(new Diff(Operation.INSERT, line));\n\t\t\t\t} else if (sign == ' ') {\n\t\t\t\t\t// Minor equality.\n\t\t\t\t\tpatch.diffs.add(new Diff(Operation.EQUAL, line));\n\t\t\t\t} else if (sign == '@') {\n\t\t\t\t\t// Start of next patch.\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\t// WTF?\n\t\t\t\t\tthrow new IllegalArgumentException(\"Invalid patch mode '\" + sign + \"' in: \"\n\t\t\t\t\t\t\t+ line);","sourceCodeStart":2328,"sourceCodeEnd":2364,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/util/diff/DiffMatchPatch.java#L2328-L2364","documentation":"Inside patch_fromText, each diff line is URL-decoded (UTF-8) before being added to the patch. If the line contains a malformed percent-escape sequence (e.g. a stray '%' not followed by two hex digits), URLDecoder.decode throws IllegalArgumentException, which is rethrown as 'Illegal escape in patch_fromText'. The library requires patch bodies to be properly percent-encoded, so any hand-crafted or corrupted line with invalid escapes fails here.","triggerScenarios":"patch_fromText reads a +/-/space-prefixed line whose content, after stripping the mode character, contains an invalid URI escape such as '%zz', '%', or a truncated '%A' at end of line.","commonSituations":"Manual editing of patch strings that introduced a literal %; double-encoding or partial decoding of patch text by an intermediate system; copying patch text through a tool that mangles percent characters; truncated storage cutting an escape sequence in half.","solutions":["Find the line reported in the message and fix or remove the malformed % escape (encode a literal % as %25).","Regenerate the patch via patch_toText so all lines are correctly URL-encoded.","URL-encode raw content yourself before building patch lines if constructing patches manually.","Validate patch lines decode cleanly (URLDecoder.decode in a probe call) before passing the whole patch string to the library."],"exampleFix":"// before\nString line = \"-100% done\"; // bare % -> illegal escape\npatches = dmp.patch_fromText(\"@@ -1,1 +1,1 @@\\n\" + line);\n// after\nString line = \"-\" + URLEncoder.encode(\"100% done\", \"UTF-8\");\npatches = dmp.patch_fromText(\"@@ -1,1 +1,1 @@\\n\" + line);","handlingStrategy":"validation","validationCode":"void validatePatchLine(String line) {\n    try {\n        new java.net.URLDecoder().decode(line.substring(1), \"UTF-8\");\n    } catch (IllegalArgumentException e) {\n        throw new IllegalArgumentException(\"Bad percent-escape in patch line: \" + line);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    patches = patch_fromText(text);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Illegal escape\")) { /* re-encode or reject */ }\n    else throw e;\n}","preventionTips":["URL-encode all diff content with URLEncoder before building patch lines.","Never insert raw '%' into patch lines; encode as %25.","Probe-decode lines yourself before calling patch_fromText."],"tags":["url-decode","patch-parse","escaping"],"backgroundTag":"invalid-url-format","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}