charmbracelet/crush · error

failed to write file: %w

Error message

failed to write file: %w

What it means

The edit tool's createNewFile path writes the new file to disk with os.WriteFile (mode 0o644) and wraps any OS-level failure in this error. It means the file content was produced but the filesystem write itself failed — this is not a permission-decision or content problem. The wrapped %w carries the underlying cause (e.g. *os.PathError).

Source

Thrown at internal/agent/tools/edit.go:165

		},
	)
	if err != nil {
		return fantasy.ToolResponse{}, err
	}
	if !p {
		resp := NewPermissionDeniedResponse()
		resp = fantasy.WithResponseMetadata(resp, EditResponseMetadata{
			OldContent: "",
			NewContent: content,
			Additions:  additions,
			Removals:   removals,
		})
		return resp, nil
	}

	err = os.WriteFile(filePath, []byte(content), 0o644)
	if err != nil {
		return fantasy.ToolResponse{}, fmt.Errorf("failed to write file: %w", err)
	}

	// File can't be in the history so we create a new file history
	_, err = edit.files.Create(edit.ctx, sessionID, filePath, "")
	if err != nil {
		// Log error but don't fail the operation
		return fantasy.ToolResponse{}, fmt.Errorf("error creating file history: %w", err)
	}

	// Add the new content to the file history
	_, err = edit.files.CreateVersion(edit.ctx, sessionID, filePath, content)
	if err != nil {
		// Log error but don't fail the operation
		slog.Error("Error creating file history version", "error", err)
	}

	edit.filetracker.RecordRead(edit.ctx, sessionID, filePath)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check the wrapped cause in the message (%w) to identify the exact OS error (ENOENT, EACCES, ENOSPC).
  2. Ensure the parent directory exists (mkdir -p) and the process has write permission on it.
  3. Free disk space or remount the filesystem read-write if ENOSPC/EROFS is reported.
  4. Verify the path is not a directory and that the tool has permission (via permission allow-list) to write it.

Example fix

// before: edit tool called with new file path but parent dir missing
err = os.WriteFile(filePath, []byte(content), 0o644)
// after: caller ensures the directory exists first
os.MkdirAll(filepath.Dir(filePath), 0o755)
err = os.WriteFile(filePath, []byte(content), 0o644)
Defensive patterns

Strategy: validation

Validate before calling

func canCreateFile(path string) error {
	if _, err := os.Stat(path); err == nil {
		return nil // exists; edit path, not create path
	}
	dir := filepath.Dir(path)
	fi, err := os.Stat(dir)
	if err != nil {
		return fmt.Errorf("parent dir missing: %w", err)
	}
	if !fi.IsDir() {
		return errors.New("parent is not a directory")
	}
	if err := syscall.Access(dir, syscall.O_RDWR); err != nil {
		return fmt.Errorf("dir not writable: %w", err)
	}
	return nil
}

Try / catch

var werr *fs.PathError
if errors.As(err, &werr) {
	log.Printf("write failed on %s: %v", werr.Path, werr.Err)
	if errors.Is(werr.Err, syscall.ENOSPC) { freeDisk() }
}

Prevention

When it happens

Trigger: Calling the edit tool with a path for a file that does not exist (create-new branch) when the write fails: parent directory does not exist, read-only filesystem, insufficient OS permissions, disk full, or the path is actually a directory-like target.

Common situations: Running the agent in a sandbox/container with a read-only workspace, typos in the path so a parent directory is missing, editing files outside an allowed mount, or the project dir living on a full tmpfs.

Related errors


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