charmbracelet/crush · error
edit %d: only the first edit can have empty old_string (for
Error message
edit %d: only the first edit can have empty old_string (for file creation)
What it means
validateEdits enforces the multi-edit file-creation convention: an empty old_string means 'create the file', which is only meaningful for the first edit. If any edit after the first has an empty old_string, validation fails with the 1-based edit index so the model/user can correct the call. No edits are applied when this fires.
Source
Thrown at internal/agent/tools/multiedit.go:120
}
// Notify LSP clients about the change
notifyLSPs(ctx, lspManager, params.FilePath)
// Wait for LSP diagnostics and add them to the response
text := fmt.Sprintf("<result>\n%s\n</result>\n", response.Content)
text += getDiagnostics(params.FilePath, lspManager)
response.Content = text
return response, nil
},
)
}
func validateEdits(edits []MultiEditOperation) error {
for i, edit := range edits {
// Only the first edit can have empty old_string (for file creation)
if i > 0 && edit.OldString == "" {
return fmt.Errorf("edit %d: only the first edit can have empty old_string (for file creation)", i+1)
}
}
return nil
}
// applyEditsToContent applies edits sequentially, collecting the ones that
// failed. It also reports whether any edit only matched after whitespace
// normalization.
func applyEditsToContent(currentContent string, edits []MultiEditOperation, startIndex int) (string, []FailedEdit, bool) {
var failedEdits []FailedEdit
var whitespaceCorrected bool
for i, edit := range edits {
newContent, corrected, err := applyEditToContent(currentContent, edit)
if err != nil {
failedEdits = append(failedEdits, FailedEdit{
Index: startIndex + i + 1,
Error: err.Error(),
Edit: edit,View on GitHub (pinned to 7944b8e522)
Solutions
- Ensure exactly one edit has empty old_string and it is the first element of the edits array
- For inserting content into an existing file, provide the surrounding old_string to replace rather than an empty one
- Move the file-creation edit to position 0 and apply subsequent edits after it
- If the file already exists, drop the empty-old_string edit and use normal search/replace edits
Example fix
// before
edits := []MultiEditOperation{
{OldString: "foo", NewString: "bar"},
{OldString: "", NewString: "created"}, // invalid: not first
}
// after
edits := []MultiEditOperation{
{OldString: "", NewString: "created"}, // creation first
{OldString: "foo", NewString: "bar"},
} Defensive patterns
Strategy: validation
Validate before calling
// mirror the library's rule before issuing the call
for i, e := range edits {
if i > 0 && e.OldString == "" {
return fmt.Errorf("edit %d cannot have empty old_string", i+1)
}
} Type guard
func isCreationEdit(i int, e MultiEditOperation) bool {
return i == 0 && e.OldString == ""
} Prevention
- When generating edit lists, put any empty-old_string edit first and only once
- For insertions in existing files, always supply the anchor text in old_string
- Have the model emit old_string explicitly (even whitespace) to avoid empty-field ambiguity
When it happens
Trigger: A multi_edit tool call where edits[1:], not edits[0], contains an operation with old_string == "" — typically a model-generated edit list where a replacement entry has an empty search string, or a creation entry misplaced after other edits.
Common situations: LLM omits old_string thinking it means 'insert here'; edit list reordered so the creation edit is not first; tool-call serialization dropping old_string fields that were empty.
Related errors
- prompt is required
- session id missing from context
- agent message id missing from context
- invalid client_id
- not a valid bedrock api key
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/ff38ad2470df4119.
Report an issue: GitHub.