Hmbown/CodeWhale · error
unexpected patch directive: {raw_line}
Error message
unexpected patch directive: {raw_line} What it means
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).
Source
Thrown at crates/tui/src/eval.rs:704
let file_rel = header
.strip_prefix("*** Update File: ")
.ok_or_else(|| anyhow!("only *** Update File patches are supported"))?;
if file_rel.contains("..") {
return Err(anyhow!("patch path must be workspace-relative"));
}
let file_path = root.join(file_rel);
let original = read_workspace_file(&file_path)?;
let had_trailing_newline = original.ends_with('\n');
let mut file_lines: Vec<String> = original.lines().map(|l| l.to_string()).collect();
let mut cursor = 0usize;
for raw_line in lines {
if raw_line == "*** End Patch" {
break;
}
if raw_line.starts_with("*** ") {
return Err(anyhow!("unexpected patch directive: {raw_line}"));
}
if raw_line.starts_with("@@") {
continue;
}
let (kind, rest) = raw_line.split_at(1);
let content = rest.to_string();
match kind {
" " => {
let Some(found) = file_lines[cursor..]
.iter()
.position(|line| line == &content)
.map(|offset| cursor + offset)
else {
return Err(anyhow!(
"patch context not found in {}: {}",
file_path.display(),View on GitHub (pinned to 8880682c63)
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
Example fix
// before: apply raw model output
apply_patch(&root, &raw)?;
// after: strip unsupported sentinels first
let cleaned: String = raw.lines()
.filter(|l| !l.starts_with("*** End of File"))
.collect::<Vec<_>>().join("\n");
apply_patch(&root, &cleaned)?; Defensive patterns
Strategy: validation
Validate before calling
for line in patch.lines().skip(2) {
if line == "*** End Patch" {
break;
}
anyhow::ensure!(
!line.starts_with("*** "),
"unexpected patch directive: {line}"
);
} Type guard
fn patch_body_is_clean(patch: &str) -> bool {
patch.lines()
.skip(2)
.take_while(|l| *l != "*** End Patch")
.all(|l| !l.starts_with("*** "))
} Prevention
- One file per patch; never batch sections
- Normalize dialects: strip `*** End of File` sentinels before applying
- Validate the whole patch shape before touching disk
When it happens
Trigger: A multi-file or multi-section patch; an OpenAI-style `*** End of File` marker appended to hunks; stray `***` comment lines inside the body.
Common situations: Model batches several file edits into one patch; dialect mixing between patch formats.
Related errors
- patch missing *** Begin Patch header
- only *** Update File patches are supported
- unsupported patch line: {raw_line}
- patch context not found in {}: {}
- patch removal mismatch in {}: expected '{}'
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/596b0315be31a552.
Report an issue: GitHub.