{"record":{"id":"596b0315be31a552","repo":"Hmbown/CodeWhale","slug":"unexpected-patch-directive-raw-line","errorCode":null,"errorMessage":"unexpected patch directive: {raw_line}","messagePattern":"unexpected patch directive: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/eval.rs","lineNumber":704,"sourceCode":"    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(\"@@\") {\n            continue;\n        }\n\n        let (kind, rest) = raw_line.split_at(1);\n        let content = rest.to_string();\n\n        match kind {\n            \" \" => {\n                let Some(found) = file_lines[cursor..]\n                    .iter()\n                    .position(|line| line == &content)\n                    .map(|offset| cursor + offset)\n                else {\n                    return Err(anyhow!(\n                        \"patch context not found in {}: {}\",\n                        file_path.display(),","sourceCodeStart":686,"sourceCodeEnd":722,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/eval.rs#L686-L722","documentation":"Inside the patch body only `@@` hunk markers and ` `/`-`/`+` lines are legal. Any other line starting with `*** ` — a second `*** Update File:` section, an `*** Add File:` section, or an `*** End of File` sentinel — is rejected as an unexpected directive (eval.rs:704).","triggerScenarios":"A multi-file or multi-section patch; an OpenAI-style `*** End of File` marker appended to hunks; stray `***` comment lines inside the body.","commonSituations":"Model batches several file edits into one patch; dialect mixing between patch formats.","solutions":["Split into one patch per file, each with its own Begin/Update/End structure","Strip `*** End of File` and other sentinels before applying","Validate body lines before apply_patch runs"],"exampleFix":"// before: apply raw model output\napply_patch(&root, &raw)?;\n// after: strip unsupported sentinels first\nlet cleaned: String = raw.lines()\n    .filter(|l| !l.starts_with(\"*** End of File\"))\n    .collect::<Vec<_>>().join(\"\\n\");\napply_patch(&root, &cleaned)?;","handlingStrategy":"validation","validationCode":"for line in patch.lines().skip(2) {\n    if line == \"*** End Patch\" {\n        break;\n    }\n    anyhow::ensure!(\n        !line.starts_with(\"*** \"),\n        \"unexpected patch directive: {line}\"\n    );\n}","typeGuard":"fn patch_body_is_clean(patch: &str) -> bool {\n    patch.lines()\n        .skip(2)\n        .take_while(|l| *l != \"*** End Patch\")\n        .all(|l| !l.starts_with(\"*** \"))\n}","tryCatchPattern":null,"preventionTips":["One file per patch; never batch sections","Normalize dialects: strip `*** End of File` sentinels before applying","Validate the whole patch shape before touching disk"],"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"}