charmbracelet/crush · error
old_string cannot be empty for content replacement
Error message
old_string cannot be empty for content replacement
What it means
applyEditToContent rejects an edit whose old_string is empty but new_string is not. Empty old_string would mean 'replace everything/nothing' and is ambiguous, so instead of guessing the tool fails the operation. Both-empty edits are treated as no-ops and pass silently.
Source
Thrown at internal/agent/tools/multiedit.go:358
OldContent: oldContent,
NewContent: currentContent,
Additions: additions,
Removals: removals,
EditsApplied: editsApplied,
EditsFailed: failedEdits,
},
), nil
}
// applyEditToContent applies a single edit, reporting whether it only matched
// after whitespace normalization.
func applyEditToContent(content string, edit MultiEditOperation) (string, bool, error) {
if edit.OldString == "" && edit.NewString == "" {
return content, false, nil
}
if edit.OldString == "" {
return "", false, fmt.Errorf("old_string cannot be empty for content replacement")
}
return findAndReplace(content, edit.OldString, edit.NewString, edit.ReplaceAll)
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Provide the exact existing text to replace in old_string; there is no empty-string insertion mode
- To insert content, include the anchor line in both old_string and new_string
- To clear a file, use the write/edit tool instead of MultiEdit
- Check the edits array in the tool input for entries missing old_string
Example fix
// before
{"old_string": "", "new_string": "func main() {}"}
// after
{"old_string": "package main\n", "new_string": "package main\n\nfunc main() {}\n"} Defensive patterns
Strategy: validation
Validate before calling
for i, e := range edits {
if e.NewString != "" && e.OldString == "" {
return fmt.Errorf("edit %d: old_string required when new_string is set", i)
}
} Try / catch
if _, _, err := applyEditsToContent(content, edits, 0); err != nil {
// inspect which operation had empty old_string and rebuild it with real context text
} Prevention
- Always supply the exact text to replace in old_string
- For insertions, anchor on an existing line in old_string
- Validate tool-call JSON before dispatching to MultiEdit
- Use the dedicated write tool for whole-file content changes
When it happens
Trigger: An edit operation in the edits array has old_string == "" while new_string is non-empty. This fails that single edit; other edits in the MultiEdit call may still apply (partial success).
Common situations: LLM emitting an insertion-style edit (old_string empty intending 'insert here') which this tool does not support; serialization dropping old_string because it was falsy; prompt/model version that uses a different replace semantics.
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/4f290fcbdaba7f89.
Report an issue: GitHub.