datawhalechina/hello-agents · error · PatchApplyError
Patch must start with '*** Begin Patch'
Error message
Patch must start with '*** Begin Patch'
What it means
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.
Source
Thrown at Co-creation-projects/YYHDBL-HelloCodeAgentCli/code_agent/executors/apply_patch_executor.py:290
返回:
List[Tuple[str, str, str]]: 操作列表,每个操作包含(操作类型, 路径, 内容)
异常:
PatchApplyError: 当补丁格式不符合要求时抛出
"""
lines = text.splitlines()
# 宽容处理:跳过前置空行/代码块围栏,找到真正的开头
while lines and lines[0].strip() in {"", "```", "```patch", "```diff", "```text"}:
lines = lines[1:]
# 如果仍未以标头开头,尝试向下寻找标头并截取
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()View on GitHub (pinned to 606a07d341)
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.
Example fix
# before patch_text = '''```diff --- a/foo.py +++ b/foo.py ```''' # unified diff -> error # after patch_text = '''*** Begin Patch *** Update File: foo.py @@ context -old +new *** End Patch'''
Defensive patterns
Strategy: validation
Validate before calling
def has_begin_marker(text: str) -> bool:
return any(l.strip() == '*** Begin Patch' for l in text.splitlines())
if not has_begin_marker(patch_text):
raise ValueError('patch lacks *** Begin Patch; regenerate in codestyle-patch format') Type guard
def looks_like_patch(text: str) -> bool:
lines = [l.strip() for l in text.splitlines()]
return '*** Begin Patch' in lines and '*** End Patch' in lines Try / catch
try:
executor.apply(patch_text)
except PatchApplyError as e:
if 'must start with' in str(e):
patch_text = extract_between_markers(patch_text) # or re-prompt the model
executor.apply(patch_text) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Patch must end with '*** End Patch'
- Unexpected patch line: {line}
- Update hunk has no context/removals; refusing to apply
- Unknown op kind: {kind}
- Absolute paths are not allowed: {rel_path}
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/8468906b07702ded.
Report an issue: GitHub.