theonedev/onedev · error · IllegalArgumentException

Illegal escape in patch_fromText: ${line}

Error message

Illegal escape in patch_fromText: ${line}

What it means

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.

Source

Thrown at server-core/src/main/java/io/onedev/server/util/diff/DiffMatchPatch.java:2346

			while (!text.isEmpty()) {
				try {
					sign = text.getFirst().charAt(0);
				} catch (IndexOutOfBoundsException e) {
					// Blank line? Whatever.
					text.removeFirst();
					continue;
				}
				line = text.getFirst().substring(1);
				line = line.replace("+", "%2B"); // decode would change all "+"
													// to " "
				try {
					line = URLDecoder.decode(line, "UTF-8");
				} catch (UnsupportedEncodingException e) {
					// Not likely on modern system.
					throw new Error("This system does not support UTF-8.", e);
				} catch (IllegalArgumentException e) {
					// Malformed URI sequence.
					throw new IllegalArgumentException("Illegal escape in patch_fromText: " + line,
							e);
				}
				if (sign == '-') {
					// Deletion.
					patch.diffs.add(new Diff(Operation.DELETE, line));
				} else if (sign == '+') {
					// Insertion.
					patch.diffs.add(new Diff(Operation.INSERT, line));
				} else if (sign == ' ') {
					// Minor equality.
					patch.diffs.add(new Diff(Operation.EQUAL, line));
				} else if (sign == '@') {
					// Start of next patch.
					break;
				} else {
					// WTF?
					throw new IllegalArgumentException("Invalid patch mode '" + sign + "' in: "
							+ line);

View on GitHub (pinned to d44925c47c)

Solutions

  1. Find the line reported in the message and fix or remove the malformed % escape (encode a literal % as %25).
  2. Regenerate the patch via patch_toText so all lines are correctly URL-encoded.
  3. URL-encode raw content yourself before building patch lines if constructing patches manually.
  4. Validate patch lines decode cleanly (URLDecoder.decode in a probe call) before passing the whole patch string to the library.

Example fix

// before
String line = "-100% done"; // bare % -> illegal escape
patches = dmp.patch_fromText("@@ -1,1 +1,1 @@\n" + line);
// after
String line = "-" + URLEncoder.encode("100% done", "UTF-8");
patches = dmp.patch_fromText("@@ -1,1 +1,1 @@\n" + line);
Defensive patterns

Strategy: validation

Validate before calling

void validatePatchLine(String line) {
    try {
        new java.net.URLDecoder().decode(line.substring(1), "UTF-8");
    } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException("Bad percent-escape in patch line: " + line);
    }
}

Try / catch

try {
    patches = patch_fromText(text);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Illegal escape")) { /* re-encode or reject */ }
    else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/3ed7f4bc35f3c43b. Report an issue: GitHub.