charmbracelet/crush · error

failed to write file: %w

Error message

failed to write file: %w

What it means

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.

Source

Thrown at internal/agent/tools/multiedit.go:218

		return fantasy.ToolResponse{}, err
	}
	if !p {
		resp := NewPermissionDeniedResponse()
		resp = fantasy.WithResponseMetadata(resp, MultiEditResponseMetadata{
			OldContent:   "",
			NewContent:   currentContent,
			Additions:    additions,
			Removals:     removals,
			EditsApplied: editsApplied,
			EditsFailed:  failedEdits,
		})
		return resp, nil
	}

	// Write the file
	err = os.WriteFile(params.FilePath, []byte(currentContent), 0o644)
	if err != nil {
		return fantasy.ToolResponse{}, fmt.Errorf("failed to write file: %w", err)
	}

	// Update file history
	_, err = edit.files.Create(edit.ctx, sessionID, params.FilePath, "")
	if err != nil {
		return fantasy.ToolResponse{}, fmt.Errorf("error creating file history: %w", err)
	}

	_, err = edit.files.CreateVersion(edit.ctx, sessionID, params.FilePath, currentContent)
	if err != nil {
		slog.Error("Error creating file history version", "error", err)
	}

	edit.filetracker.RecordRead(edit.ctx, sessionID, params.FilePath)

	var message string
	if len(failedEdits) > 0 {
		message = fmt.Sprintf("File created with %d of %d edits: %s (%d edit(s) failed)", editsApplied, len(params.Edits), params.FilePath, len(failedEdits))

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Read the wrapped OS error in the message and address it (chmod/chown the file or directory)
  2. Check that file_path is not an existing directory
  3. Free disk space if ENOSPC
  4. Re-run the edit once the filesystem condition is resolved

Example fix

// before
# ls -l src
src/new.go: directory (target of write)
// after
# remove/rename the blocking directory or choose a different file path, then retry the MultiEdit call
Defensive patterns

Strategy: try-catch

Validate before calling

if st, err := os.Stat(path); err == nil && st.IsDir() {
    return fmt.Errorf("%s is a directory", path)
}
if err := unix.Access(filepath.Dir(path), unix.W_OK); err != nil {
    return fmt.Errorf("no write permission in %s", filepath.Dir(path))
}

Try / catch

var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr, syscall.ENOSPC) {
    // free disk space and retry
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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