Hmbown/CodeWhale · error

only *** Update File patches are supported

Error message

only *** Update File patches are supported

What it means

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.

Source

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

        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" {
            break;
        }
        if raw_line.starts_with("*** ") {
            return Err(anyhow!("unexpected patch directive: {raw_line}"));
        }
        if raw_line.starts_with("@@") {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Express the change as an update to an existing file
  2. 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
  3. Split multi-operation patches into single-file updates

Example fix

*** before
*** Begin Patch
*** Add File: notes.md
+hello
*** after (create empty notes.md first, then)
*** Begin Patch
*** Update File: notes.md
@@
+hello
*** End Patch
Defensive patterns

Strategy: validation

Validate before calling

let mut lines = patch.lines();
anyhow::ensure!(lines.next() == Some("*** Begin Patch"), "bad patch header");
anyhow::ensure!(
    lines.next().unwrap_or_default().starts_with("*** Update File: "),
    "only single-file update patches are supported here"
);

Type guard

fn is_update_patch(patch: &str) -> bool {
    let mut lines = patch.lines();
    lines.next() == Some("*** Begin Patch")
        && lines.next().unwrap_or_default().starts_with("*** Update File: ")
}

Prevention

When it happens

Trigger: The patch tries to create, delete, or move a file instead of updating one; the header line is blank; the directive spelling differs.

Common situations: Model mixes the OpenAI apply-patch dialect (Add/Delete/Move) into its output; multi-operation edits batched into one patch.

Related errors


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