{"record":{"id":"ff38ad2470df4119","repo":"charmbracelet/crush","slug":"edit-d-only-the-first-edit-can-have-empty-old-st","errorCode":null,"errorMessage":"edit %d: only the first edit can have empty old_string (for file creation)","messagePattern":"edit (.+?): only the first edit can have empty old_string \\(for file creation\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/multiedit.go","lineNumber":120,"sourceCode":"\t\t\t}\n\n\t\t\t// Notify LSP clients about the change\n\t\t\tnotifyLSPs(ctx, lspManager, params.FilePath)\n\n\t\t\t// Wait for LSP diagnostics and add them to the response\n\t\t\ttext := fmt.Sprintf(\"<result>\\n%s\\n</result>\\n\", response.Content)\n\t\t\ttext += getDiagnostics(params.FilePath, lspManager)\n\t\t\tresponse.Content = text\n\t\t\treturn response, nil\n\t\t},\n\t)\n}\n\nfunc validateEdits(edits []MultiEditOperation) error {\n\tfor i, edit := range edits {\n\t\t// Only the first edit can have empty old_string (for file creation)\n\t\tif i > 0 && edit.OldString == \"\" {\n\t\t\treturn fmt.Errorf(\"edit %d: only the first edit can have empty old_string (for file creation)\", i+1)\n\t\t}\n\t}\n\treturn nil\n}\n\n// applyEditsToContent applies edits sequentially, collecting the ones that\n// failed. It also reports whether any edit only matched after whitespace\n// normalization.\nfunc applyEditsToContent(currentContent string, edits []MultiEditOperation, startIndex int) (string, []FailedEdit, bool) {\n\tvar failedEdits []FailedEdit\n\tvar whitespaceCorrected bool\n\tfor i, edit := range edits {\n\t\tnewContent, corrected, err := applyEditToContent(currentContent, edit)\n\t\tif err != nil {\n\t\t\tfailedEdits = append(failedEdits, FailedEdit{\n\t\t\t\tIndex: startIndex + i + 1,\n\t\t\t\tError: err.Error(),\n\t\t\t\tEdit:  edit,","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/multiedit.go#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nedits := []MultiEditOperation{\n  {OldString: \"foo\", NewString: \"bar\"},\n  {OldString: \"\", NewString: \"created\"}, // invalid: not first\n}\n// after\nedits := []MultiEditOperation{\n  {OldString: \"\", NewString: \"created\"}, // creation first\n  {OldString: \"foo\", NewString: \"bar\"},\n}","handlingStrategy":"validation","validationCode":"// mirror the library's rule before issuing the call\nfor i, e := range edits {\n    if i > 0 && e.OldString == \"\" {\n        return fmt.Errorf(\"edit %d cannot have empty old_string\", i+1)\n    }\n}","typeGuard":"func isCreationEdit(i int, e MultiEditOperation) bool {\n    return i == 0 && e.OldString == \"\"\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["validation","edit","tool-parameters"],"backgroundTag":"invalid-tool-arguments","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}