micro-editor/micro · error

Error: %s is a directory and cannot be saved

Error message

Error: %s is a directory and cannot be saved

What it means

Returned by (*Buffer).saveToFile (internal/buffer/buffer save.go:280) when os.Stat on the save target succeeds and the target is a directory. Micro only writes regular files, so saving over a directory path is refused before open/write. Note this fires for SaveAs targets and for the original path when it got replaced by a directory since the buffer was opened.

Source

Thrown at internal/buffer/save.go:280

			b.insert(end, []byte{'\n'})
		}
	}

	filename, err = util.ReplaceHome(filename)
	if err != nil {
		return err
	}

	newFile := false
	fileInfo, err := os.Stat(filename)
	if err != nil {
		if !errors.Is(err, fs.ErrNotExist) {
			return err
		}
		newFile = true
	}
	if err == nil && fileInfo.IsDir() {
		return errors.New("Error: " + filename + " is a directory and cannot be saved")
	}
	if err == nil && !fileInfo.Mode().IsRegular() {
		return errors.New("Error: " + filename + " is not a regular file and cannot be saved")
	}

	absFilename := util.ResolvePath(filename)

	// Get the leading path to the file | "." is returned if there's no leading path provided
	if dirname := filepath.Dir(absFilename); dirname != "." {
		// Check if the parent dirs don't exist
		if _, statErr := os.Stat(dirname); errors.Is(statErr, fs.ErrNotExist) {
			// Prompt to make sure they want to create the dirs that are missing
			if b.Settings["mkparents"].(bool) {
				// Create all leading dir(s) since they don't exist
				if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil {
					// If there was an error creating the dirs
					return mkdirallErr
				}

View on GitHub (pinned to 1c8b82b32e)

Solutions

  1. Give the full file path: :saveas /tmp/notes.txt (no trailing slash)
  2. If the original path became a directory: mv it aside or SaveAs to a new filename, then fix your tree
  3. In code, stat the destination and require !IsDir() before calling Save/SaveAs (see validation snippet)

Example fix

// before
:saveas ~/projects/
# Error: /home/u/projects is a directory and cannot be saved

// after
:saveas ~/projects/main.go
Defensive patterns

Strategy: validation

Validate before calling

// Verify the save target is a writable regular-file slot before SaveAs
func saveTargetOk(path string) error {
    fi, err := os.Stat(path)
    if err != nil { return nil }              // new file: fine
    if fi.IsDir() { return fmt.Errorf("%s is a directory", path) }
    if !fi.Mode().IsRegular() { return fmt.Errorf("%s is not a regular file", path) }
    return nil
}

Try / catch

if err := h.Buf.SaveAs(target); err != nil {
    if strings.HasSuffix(err.Error(), "is a directory and cannot be saved") {
        InfoBar.Error("append a filename: " + filepath.Join(target, "file.txt"))
        return
    }
    return err
}

Prevention

When it happens

Trigger: :saveas /tmp (existing dir), :saveas some/dir where some/dir exists as a directory; or the buffer's original file was deleted and a same-named directory created (common after 'rm x && mkdir x'), then Ctrl-s is pressed.

Common situations: Save dialogs/commands given a folder instead of a full file path; deployment scripts that turn a file location into a directory; typos with trailing slash: ':saveas backup/'

Related errors


AI-assisted analysis of micro-editor/micro@1c8b82b32e (2026-08-15). Data as JSON: /api/errors/406a6b90fdd1bbb1. Report an issue: GitHub.