{"record":{"id":"4eca5e825fdaa0c2","repo":"Hmbown/CodeWhale","slug":"only-update-file-patches-are-supported","errorCode":null,"errorMessage":"only *** Update File patches are supported","messagePattern":"only \\*\\*\\* Update File patches are supported","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/eval.rs","lineNumber":688,"sourceCode":"        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\" {\n            break;\n        }\n        if raw_line.starts_with(\"*** \") {\n            return Err(anyhow!(\"unexpected patch directive: {raw_line}\"));\n        }\n        if raw_line.starts_with(\"@@\") {","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/eval.rs#L670-L706","documentation":"Only update patches are supported: line 2 must be `*** Update File: <workspace-relative path>` (eval.rs:688). A missing second line or any other directive — `*** Add File:`, `*** Delete File:`, `*** Move to:` — fails before the file is opened.","triggerScenarios":"The patch tries to create, delete, or move a file instead of updating one; the header line is blank; the directive spelling differs.","commonSituations":"Model mixes the OpenAI apply-patch dialect (Add/Delete/Move) into its output; multi-operation edits batched into one patch.","solutions":["Express the change as an update to an existing file","For new files, create the file first (write initial content via the write path), then send an update patch — or skip apply_patch and write directly","Split multi-operation patches into single-file updates"],"exampleFix":"*** before\n*** Begin Patch\n*** Add File: notes.md\n+hello\n*** after (create empty notes.md first, then)\n*** Begin Patch\n*** Update File: notes.md\n@@\n+hello\n*** End Patch","handlingStrategy":"validation","validationCode":"let mut lines = patch.lines();\nanyhow::ensure!(lines.next() == Some(\"*** Begin Patch\"), \"bad patch header\");\nanyhow::ensure!(\n    lines.next().unwrap_or_default().starts_with(\"*** Update File: \"),\n    \"only single-file update patches are supported here\"\n);","typeGuard":"fn is_update_patch(patch: &str) -> bool {\n    let mut lines = patch.lines();\n    lines.next() == Some(\"*** Begin Patch\")\n        && lines.next().unwrap_or_default().starts_with(\"*** Update File: \")\n}","tryCatchPattern":null,"preventionTips":["State the single-file-update contract in the model prompt","Handle file creation and deletion through write/delete tool paths, not patches","Validate the second-line directive before applying"],"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"}