Hmbown/CodeWhale · error

patch missing *** Begin Patch header

Error message

patch missing *** Begin Patch header

What it means

`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.

Source

Thrown at crates/tui/src/eval.rs:682

    Ok(SearchResult { matches })
}

fn append_workspace_file(path: &Path, line: &str) -> Result<()> {
    let mut content = read_workspace_file(path)?;
    if !content.ends_with('\n') {
        content.push('\n');
    }
    content.push_str(line);
    content.push('\n');
    fs::write(path, content).with_context(|| format!("failed to write {}", path.display()))
}

fn apply_patch(root: &Path, patch: &str) -> Result<()> {
    let mut lines = patch.lines();

    let begin = lines.next().unwrap_or_default();
    if begin != "*** Begin Patch" {
        return Err(anyhow!("patch missing *** Begin Patch header"));
    }

    let header = lines.next().unwrap_or_default();
    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" {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Trim leading whitespace/newlines and ensure line 1 is exactly `*** Begin Patch`
  2. Convert foreign formats (unified diff) before calling apply_patch
  3. Validate the header before applying

Example fix

// before
let patch = raw_model_output;
// after
let patch = raw_model_output.trim_start();
assert_eq!(patch.lines().next(), Some("*** Begin Patch"));
Defensive patterns

Strategy: validation

Validate before calling

fn patch_has_begin_header(patch: &str) -> bool {
    patch.trim_start().lines().next() == Some("*** Begin Patch")
}
anyhow::ensure!(
    patch_has_begin_header(&patch),
    "model output lacks the *** Begin Patch header"
);

Type guard

fn patch_has_begin_header(patch: &str) -> bool {
    patch.trim_start().lines().next() == Some("*** Begin Patch")
}

Try / catch

match apply_patch(&root, &patch) {
    Err(err) if err.to_string().contains("*** Begin Patch") => {
        // re-prompt the model for the full patch with the exact header
    }
    other => other?,
}

Prevention

When it happens

Trigger: 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.

Common situations: LLM emits a different diff dialect; a prompt template or transport mangles the first line; hand-written patches missing the sentinel.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/5dbc5e1ff05c9df6. Report an issue: GitHub.