{"record":{"id":"5dbc5e1ff05c9df6","repo":"Hmbown/CodeWhale","slug":"patch-missing-begin-patch-header","errorCode":null,"errorMessage":"patch missing *** Begin Patch header","messagePattern":"patch missing \\*\\*\\* Begin Patch header","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/eval.rs","lineNumber":682,"sourceCode":"    Ok(SearchResult { matches })\n}\n\nfn append_workspace_file(path: &Path, line: &str) -> Result<()> {\n    let mut content = read_workspace_file(path)?;\n    if !content.ends_with('\\n') {\n        content.push('\\n');\n    }\n    content.push_str(line);\n    content.push('\\n');\n    fs::write(path, content).with_context(|| format!(\"failed to write {}\", path.display()))\n}\n\nfn apply_patch(root: &Path, patch: &str) -> Result<()> {\n    let mut lines = patch.lines();\n\n    let begin = lines.next().unwrap_or_default();\n    if begin != \"*** Begin Patch\" {\n        return Err(anyhow!(\"patch missing *** Begin Patch header\"));\n    }\n\n    let header = lines.next().unwrap_or_default();\n    let file_rel = header\n        .strip_prefix(\"*** Update File: \")\n        .ok_or_else(|| anyhow!(\"only *** Update File patches are supported\"))?;\n    if file_rel.contains(\"..\") {\n        return Err(anyhow!(\"patch path must be workspace-relative\"));\n    }\n\n    let file_path = root.join(file_rel);\n    let original = read_workspace_file(&file_path)?;\n    let had_trailing_newline = original.ends_with('\\n');\n    let mut file_lines: Vec<String> = original.lines().map(|l| l.to_string()).collect();\n\n    let mut cursor = 0usize;\n    for raw_line in lines {\n        if raw_line == \"*** End Patch\" {","sourceCodeStart":664,"sourceCodeEnd":700,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/eval.rs#L664-L700","documentation":"`apply_patch` implements a strict single-file patch format. The very first line must equal `*** Begin Patch` exactly (eval.rs:682-686); anything else — including an empty first line caused by a leading newline — aborts before any file is read or written.","triggerScenarios":"Model output omits the header; the patch string starts with a newline or whitespace; a unified diff or markdown-fenced block is passed instead of the expected format.","commonSituations":"LLM emits a different diff dialect; a prompt template or transport mangles the first line; hand-written patches missing the sentinel.","solutions":["Trim leading whitespace/newlines and ensure line 1 is exactly `*** Begin Patch`","Convert foreign formats (unified diff) before calling apply_patch","Validate the header before applying"],"exampleFix":"// before\nlet patch = raw_model_output;\n// after\nlet patch = raw_model_output.trim_start();\nassert_eq!(patch.lines().next(), Some(\"*** Begin Patch\"));","handlingStrategy":"validation","validationCode":"fn patch_has_begin_header(patch: &str) -> bool {\n    patch.trim_start().lines().next() == Some(\"*** Begin Patch\")\n}\nanyhow::ensure!(\n    patch_has_begin_header(&patch),\n    \"model output lacks the *** Begin Patch header\"\n);","typeGuard":"fn patch_has_begin_header(patch: &str) -> bool {\n    patch.trim_start().lines().next() == Some(\"*** Begin Patch\")\n}","tryCatchPattern":"match apply_patch(&root, &patch) {\n    Err(err) if err.to_string().contains(\"*** Begin Patch\") => {\n        // re-prompt the model for the full patch with the exact header\n    }\n    other => other?,\n}","preventionTips":["Validate patch shape before touching disk","Trim model output before parsing","Re-prompt with the exact format contract on failure instead of patching text ad hoc"],"tags":["patch","parsing","eval","llm-output"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}