datawhalechina/hello-agents · error · PatchApplyError
Patch must end with '*** End Patch'
Error message
Patch must end with '*** End Patch'
What it means
Raised by the patch parser when the text, after trimming trailing blanks/fences and back-scanning for the last '*** End Patch' marker, does not end with that exact line. It complements the Begin-Patch check and exists because truncated LLM output commonly loses the closing fence.
Source
Thrown at Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py:301
# 如果仍未以标头开头,尝试向下寻找标头并截取
if lines and lines[0].strip() != "*** Begin Patch":
for idx, l in enumerate(lines):
if l.strip() == "*** Begin Patch":
lines = lines[idx:]
break
if not lines or lines[0].strip() != "*** Begin Patch":
raise PatchApplyError("Patch must start with '*** Begin Patch'")
# 同样跳过结尾的围栏/空行
while lines and lines[-1].strip() in {"", "```"}:
lines = lines[:-1]
if not lines or lines[-1].strip() != "*** End Patch":
# 如果末尾未对齐,尝试在中间找到最后一个 End 标记
for idx in range(len(lines) - 1, -1, -1):
if lines[idx].strip() == "*** End Patch":
lines = lines[: idx + 1]
break
if not lines or lines[-1].strip() != "*** End Patch":
raise PatchApplyError("Patch must end with '*** End Patch'")
ops: List[Tuple[str, str, str]] = []
i = 1
while i < len(lines) - 1:
line = lines[i]
if line.startswith("*** Add File: "):
path = line[len("*** Add File: ") :].strip()
i += 1
buf: List[str] = []
while i < len(lines) - 1 and not lines[i].startswith("*** "):
# 兼容两种格式:
# 1) 规范形式:以 '+' 开头
# 2) 宽松形式:直接给出正文(模型有时会省略 '+')
if lines[i].startswith("+"):
buf.append(lines[i][1:] + "\n")
else:
buf.append(lines[i] + "\n")
i += 1View on GitHub (pinned to 606a07d341)
Solutions
- Append a literal '*** End Patch' line to the patch text before parsing.
- Raise max_tokens / reduce patch size so the model can finish the block.
- If truncation is routine, append the closer programmatically after detecting a Begin without End.
Example fix
# before
patch_text = model_output # truncated, no End marker
# after
if '*** End Patch' not in patch_text:
patch_text = patch_text.rstrip() + '\n*** End Patch'
result = executor.apply(patch_text) Defensive patterns
Strategy: validation
Validate before calling
lines = [l.strip() for l in patch_text.splitlines()]
if '*** End Patch' not in lines:
if '*** Begin Patch' in lines: # truncated but started
patch_text = patch_text.rstrip() + '\n*** End Patch'
else:
raise ValueError('no patch markers at all; regenerate') Try / catch
try:
executor.apply(patch_text)
except PatchApplyError as e:
if "must end with" in str(e):
patch_text = patch_text.rstrip() + '\n*** End Patch'
executor.apply(patch_text)
raise Prevention
- Set max_tokens high enough for the largest expected patch.
- Auto-append the End marker when truncation is detected (Begin present, End absent).
- Log finish_reason from the model; 'length' predicts truncated patches.
When it happens
Trigger: Model output truncated by max_tokens so '*** End Patch' was never emitted; a trailing code fence '```' after End is fine, but a missing End marker entirely is not; the End line has trailing whitespace variants — strip() handles those — or was replaced with '*** END PATCH'.
Common situations: max_tokens too small for large patches; streaming code that stops on first '*** End Patch' occurrence then reassembles wrongly; users hand-editing patches and dropping the closer.
Related errors
- Patch must start with '*** Begin Patch'
- Unexpected patch line: {line}
- Update hunk has no context/removals; refusing to apply
- 无法从输出解析 note_id: {output}
- 无法从输出解析 note_id: {output}
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/870d6aff0b73bc42.
Report an issue: GitHub.