{"record":{"id":"84bd51112edda251","repo":"charmbracelet/crush","slug":"failed-to-write-file-w-84bd51","errorCode":null,"errorMessage":"failed to write file: %w","messagePattern":"failed to write file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/multiedit.go","lineNumber":218,"sourceCode":"\t\treturn fantasy.ToolResponse{}, err\n\t}\n\tif !p {\n\t\tresp := NewPermissionDeniedResponse()\n\t\tresp = fantasy.WithResponseMetadata(resp, MultiEditResponseMetadata{\n\t\t\tOldContent:   \"\",\n\t\t\tNewContent:   currentContent,\n\t\t\tAdditions:    additions,\n\t\t\tRemovals:     removals,\n\t\t\tEditsApplied: editsApplied,\n\t\t\tEditsFailed:  failedEdits,\n\t\t})\n\t\treturn resp, nil\n\t}\n\n\t// Write the file\n\terr = os.WriteFile(params.FilePath, []byte(currentContent), 0o644)\n\tif err != nil {\n\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"failed to write file: %w\", err)\n\t}\n\n\t// Update file history\n\t_, err = edit.files.Create(edit.ctx, sessionID, params.FilePath, \"\")\n\tif err != nil {\n\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"error creating file history: %w\", err)\n\t}\n\n\t_, err = edit.files.CreateVersion(edit.ctx, sessionID, params.FilePath, currentContent)\n\tif err != nil {\n\t\tslog.Error(\"Error creating file history version\", \"error\", err)\n\t}\n\n\tedit.filetracker.RecordRead(edit.ctx, sessionID, params.FilePath)\n\n\tvar message string\n\tif len(failedEdits) > 0 {\n\t\tmessage = fmt.Sprintf(\"File created with %d of %d edits: %s (%d edit(s) failed)\", editsApplied, len(params.Edits), params.FilePath, len(failedEdits))","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/multiedit.go#L200-L236","documentation":"os.WriteFile failed while persisting the newly created (or edited) file to disk after the edits were applied in memory. The error is wrapped verbatim from the OS, so the cause (permissions, disk full, is-a-directory) is in the %w suffix. All applied edits are discarded at this point because the write failed.","triggerScenarios":"os.WriteFile(params.FilePath, content, 0o644) returns an error: target path is an existing directory (EISDIR), no write permission (EACCES), disk full (ENOSPC), or the path was deleted/replaced concurrently.","commonSituations":"Agent tries to write over a directory path; container user lacks write access to the repo; quota/ENOSPC on the volume; file locked by another process on some filesystems.","solutions":["Read the wrapped OS error in the message and address it (chmod/chown the file or directory)","Check that file_path is not an existing directory","Free disk space if ENOSPC","Re-run the edit once the filesystem condition is resolved"],"exampleFix":"// before\n# ls -l src\nsrc/new.go: directory (target of write)\n// after\n# remove/rename the blocking directory or choose a different file path, then retry the MultiEdit call","handlingStrategy":"try-catch","validationCode":"if st, err := os.Stat(path); err == nil && st.IsDir() {\n    return fmt.Errorf(\"%s is a directory\", path)\n}\nif err := unix.Access(filepath.Dir(path), unix.W_OK); err != nil {\n    return fmt.Errorf(\"no write permission in %s\", filepath.Dir(path))\n}","typeGuard":null,"tryCatchPattern":"var perr *fs.PathError\nif errors.As(err, &perr) && errors.Is(perr, syscall.ENOSPC) {\n    // free disk space and retry\n}","preventionTips":["Confirm target isn't an existing directory before editing","Watch disk usage in CI/container environments","Run the agent as a user with write access to the repo","Handle EACCES/ENOSPC distinctly when wrapping"],"tags":["filesystem","permissions","tool"],"backgroundTag":"file-write-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}