Hmbown/CodeWhale · error · ToolError
Edit refused: it would leave
Error message
Edit refused: it would leave {display_path} unparseable — {issue}. Nothing was written; the file is unchanged. Recovery: re-read the file with File action="read", check the replacement for unbalanced delimiters or a truncated block, and retry. What it means
The edit tool's syntax guard parsed the post-edit content of a .rs file with `syn::parse_file` and it failed, so the write was refused. The gate only fires when the file was parseable BEFORE the edit, guaranteeing the proposed edit is what breaks the file. Nothing was written to disk.
Solutions
- Re-read the file with File action="read" to get its unchanged, valid content.
- Locate the reported line/column and compare your replacement against the original block.
- Fix unbalanced braces/parens or the truncated block and retry the edit.
- Split a large risky edit into smaller syntactically complete chunks.
Example fix
// before (truncated replacement leaves brace unbalanced)
fn f() {
let x = 1;
// after (complete block)
fn f() {
let x = 1;
} Defensive patterns
Strategy: validation
Validate before calling
// validate the post-edit Rust content before calling the edit tool
if syn::parse_file(&after_content).is_err() {
// fix braces/truncated block first; the edit would be refused
} Try / catch
// caller of the edit tool: on refusal, re-read and retry
match tool.edit(path, old, new) {
Err(e) if e.to_string().contains("Edit refused: it would leave") => {
let fresh = tool.read(path)?; // file is byte-for-byte unchanged
// diagnose reported line/column and retry with corrected replacement
}
other => other?,
} Prevention
- Always re-read the file before constructing a replacement.
- Keep replacements syntactically complete — never truncate mid-block.
- Prefer narrow, single-construct edits over file-spanning replaces.
- Run rustfmt or `rustc --emit=metadata` on the snippet mentally/locally before applying.
When it happens
Trigger: Calling the File edit action on a Rust file whose resulting content fails `syn::parse_file` (rust file was valid before, would not parse after). check_rust reports the first syn diagnostic with 1-based line/column.
Common situations: A model/agent replacement block is truncated mid-function; unbalanced braces from a partial replace; a replacement string accidentally containing invalid token sequences; edit anchors spanning half of a match arm.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- A pinned task provider requires an explicit model
- agent profile provider cannot be empty
- agent profile provider must be a simple provider id
- Antigravity cloud-code request has no text contents
- err
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/82f89d7edf338354.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/syntax_check.rs:160
};
// Fail open on a file that was already broken (or is brand new): the gate
// exists to catch the edit that *introduces* a syntax error, never to
// strand a model that is repairing one.
let Some(before) = before else {
return Ok(());
};
if syntax_check(path, before).is_some() {
return Ok(());
}
Err(ToolError::execution_failed(format!(
"Edit refused: it would leave {display_path} unparseable — {issue}. Nothing was written; \
the file is unchanged. Recovery: re-read the file with File action=\"read\", check the \
replacement for unbalanced delimiters or a truncated block, and retry."
)))
}
fn check_rust(source: &str) -> Option<SyntaxIssue> {
let error = syn::parse_file(source).err()?;
// A `syn::Error` can carry several diagnostics; the first is the earliest
// and the one worth showing.
let first = error.into_iter().next()?;
let start = first.span().start();
Some(SyntaxIssue {
language: SyntaxLanguage::Rust,
line: start.line,
// `proc-macro2` columns are 0-based; editors and rustc are not.
column: start.column.saturating_add(1),
message: first.to_string(),
})
}
fn check_toml(source: &str) -> Option<SyntaxIssue> {
let error = source.parse::<toml_edit::DocumentMut>().err()?;
let (line, column) = error
.span()
.map_or((1, 1), |span| line_column(source, span.start));View on GitHub (pinned to 73e0f67d83)