{"record":{"id":"8468906b07702ded","repo":"datawhalechina/hello-agents","slug":"patch-must-start-with-begin-patch","errorCode":null,"errorMessage":"Patch must start with '*** Begin Patch'","messagePattern":"Patch must start with '\\*\\*\\* Begin Patch'","errorType":"exception","errorClass":"PatchApplyError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py","lineNumber":290,"sourceCode":"            \n        返回:\n            List[Tuple[str, str, str]]: 操作列表，每个操作包含(操作类型, 路径, 内容)\n            \n        异常:\n            PatchApplyError: 当补丁格式不符合要求时抛出\n        \"\"\"\n        lines = text.splitlines()\n        # 宽容处理：跳过前置空行/代码块围栏，找到真正的开头\n        while lines and lines[0].strip() in {\"\", \"```\", \"```patch\", \"```diff\", \"```text\"}:\n            lines = lines[1:]\n        # 如果仍未以标头开头，尝试向下寻找标头并截取\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()","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py#L272-L308","documentation":"Raised by the patch parser when, after lenient preprocessing (skipping leading blank lines and ```/```patch/```diff/```text fences, and scanning forward for the marker), the text still does not begin with a line reading exactly '*** Begin Patch'. The parser tolerates LLM fencing but requires the canonical codestyle-patch header somewhere at the start.","triggerScenarios":"Passing raw model output that contains no '*** Begin Patch' line at all; a header with typos ('** Begin Patch', '*** begin patch'); nested code fences that make the scan miss the marker; an empty string.","commonSituations":"LLM omits the fence format and emits a unified diff instead; chat responses with the patch buried after prose (the forward scan only finds the marker if it exists, but leading non-fence prose lines before the marker are skipped only by the scan — prose before a valid marker is handled, missing markers are not); string concatenation dropping the first line.","solutions":["Ensure the patch text literally contains '*** Begin Patch' as its own line and '*** End Patch' at the end.","If the model returned a unified diff, convert it or re-prompt with the required format in the system prompt.","Strip surrounding prose/fences before calling apply; the parser helps but cannot invent the header."],"exampleFix":"# before\npatch_text = '''```diff\n--- a/foo.py\n+++ b/foo.py\n```'''  # unified diff -> error\n\n# after\npatch_text = '''*** Begin Patch\n*** Update File: foo.py\n@@\n context\n-old\n+new\n*** End Patch'''","handlingStrategy":"validation","validationCode":"def has_begin_marker(text: str) -> bool:\n    return any(l.strip() == '*** Begin Patch' for l in text.splitlines())\n\nif not has_begin_marker(patch_text):\n    raise ValueError('patch lacks *** Begin Patch; regenerate in codestyle-patch format')","typeGuard":"def looks_like_patch(text: str) -> bool:\n    lines = [l.strip() for l in text.splitlines()]\n    return '*** Begin Patch' in lines and '*** End Patch' in lines","tryCatchPattern":"try:\n    executor.apply(patch_text)\nexcept PatchApplyError as e:\n    if 'must start with' in str(e):\n        patch_text = extract_between_markers(patch_text)  # or re-prompt the model\n        executor.apply(patch_text)","preventionTips":["Include the exact patch grammar in the model's system prompt with a one-shot example.","Validate for the Begin/End markers before calling apply.","Prefer programmatic patch builders over free-form model text where possible."],"tags":["patch","parsing","llm-output","apply-patch"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}