{"record":{"id":"870d6aff0b73bc42","repo":"datawhalechina/hello-agents","slug":"patch-must-end-with-end-patch","errorCode":null,"errorMessage":"Patch must end with '*** End Patch'","messagePattern":"Patch must end with '\\*\\*\\* End Patch'","errorType":"exception","errorClass":"PatchApplyError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py","lineNumber":301,"sourceCode":"        # 如果仍未以标头开头，尝试向下寻找标头并截取\n        if lines and lines[0].strip() != \"*** Begin Patch\":\n            for idx, l in enumerate(lines):\n                if l.strip() == \"*** Begin Patch\":\n                    lines = lines[idx:]\n                    break\n        if not lines or lines[0].strip() != \"*** Begin Patch\":\n            raise PatchApplyError(\"Patch must start with '*** Begin Patch'\")\n        # 同样跳过结尾的围栏/空行\n        while lines and lines[-1].strip() in {\"\", \"```\"}:\n            lines = lines[:-1]\n        if not lines or lines[-1].strip() != \"*** End Patch\":\n            # 如果末尾未对齐，尝试在中间找到最后一个 End 标记\n            for idx in range(len(lines) - 1, -1, -1):\n                if lines[idx].strip() == \"*** End Patch\":\n                    lines = lines[: idx + 1]\n                    break\n        if not lines or lines[-1].strip() != \"*** End Patch\":\n            raise PatchApplyError(\"Patch must end with '*** End Patch'\")\n\n        ops: List[Tuple[str, str, str]] = []\n        i = 1\n        while i < len(lines) - 1:\n            line = lines[i]\n            if line.startswith(\"*** Add File: \"):\n                path = line[len(\"*** Add File: \") :].strip()\n                i += 1\n                buf: List[str] = []\n                while i < len(lines) - 1 and not lines[i].startswith(\"*** \"):\n                    # 兼容两种格式：\n                    # 1) 规范形式：以 '+' 开头\n                    # 2) 宽松形式：直接给出正文（模型有时会省略 '+')\n                    if lines[i].startswith(\"+\"):\n                        buf.append(lines[i][1:] + \"\\n\")\n                    else:\n                        buf.append(lines[i] + \"\\n\")\n                    i += 1","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py#L283-L319","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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."],"exampleFix":"# before\npatch_text = model_output  # truncated, no End marker\n\n# after\nif '*** End Patch' not in patch_text:\n    patch_text = patch_text.rstrip() + '\\n*** End Patch'\nresult = executor.apply(patch_text)","handlingStrategy":"validation","validationCode":"lines = [l.strip() for l in patch_text.splitlines()]\nif '*** End Patch' not in lines:\n    if '*** Begin Patch' in lines:  # truncated but started\n        patch_text = patch_text.rstrip() + '\\n*** End Patch'\n    else:\n        raise ValueError('no patch markers at all; regenerate')","typeGuard":null,"tryCatchPattern":"try:\n    executor.apply(patch_text)\nexcept PatchApplyError as e:\n    if \"must end with\" in str(e):\n        patch_text = patch_text.rstrip() + '\\n*** End Patch'\n        executor.apply(patch_text)\n    raise","preventionTips":["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."],"tags":["patch","parsing","truncation","llm-output"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}