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

  1. Provide the exact existing text to replace in old_string; there is no empty-string insertion mode
  2. To insert content, include the anchor line in both old_string and new_string
  3. To clear a file, use the write/edit tool instead of MultiEdit
  4. 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

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


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/4f290fcbdaba7f89. Report an issue: GitHub.