micro-editor/micro · error

Error: %s is not a regular file and cannot be saved

Error message

Error: %s is not a regular file and cannot be saved

What it means

Thrown by buffer.Save/SaveAs when the target path exists, is not a directory, but is also not a regular file (os.Stat succeeded and fileInfo.Mode().IsRegular() is false). This covers character/block devices, named pipes (FIFOs), unix sockets, and other special files. Micro refuses to write buffer contents to such nodes because the save loop expects a seekable, plain file.

Source

Thrown at internal/buffer/save.go:283

	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
				}
			} else {
				return errors.New("Parent dirs don't exist, enable 'mkparents' for auto creation")
			}

View on GitHub (pinned to 1c8b82b32e)

Solutions

  1. Save to a real file path: pick a name that does not exist yet, or an existing regular file.
  2. If you intended /dev/null semantics, save to a scratch regular file instead and let your shell/tooling discard it.
  3. Remove the special file (rm /path/to/fifo) if it was created accidentally and you own it.
  4. If you truly must write to a device node, do it outside micro (e.g. write file then `cat file > /dev/...`).

Example fix

// before (micro command bar):
> save /dev/null
// error: /dev/null is not a regular file and cannot be saved

// after:
> save /tmp/scratch.txt
Defensive patterns

Strategy: validation

Validate before calling

func canSaveTo(path string) bool {
    fi, err := os.Stat(path)
    if errors.Is(err, fs.ErrNotExist) {
        return true // new file is fine
    }
    if err != nil {
        return false
    }
    return fi.Mode().IsRegular()
}

// before b.SaveAs(path):
if !canSaveTo("/dev/null") { /* pick another path */ }

Type guard

func isRegularFile(path string) (bool, error) {
    fi, err := os.Stat(path)
    if err != nil {
        return false, err
    }
    return fi.Mode().IsRegular(), nil
}

Try / catch

if err := buf.SaveAs(p); err != nil {
    if strings.Contains(err.Error(), "is not a regular file") {
        // prompt user for a different path or shell out to write elsewhere
    }
}

Prevention

When it happens

Trigger: Calling SaveAs on /dev/null or /dev/zero (character devices), on a named pipe created with mkfifo, on a unix socket file, or on a device node under /dev. Any os.Stat(filename) that succeeds with a non-regular, non-directory mode hits the `!fileInfo.Mode().IsRegular()` branch in internal/buffer/save.go:283.

Common situations: Users piping micro output toward /dev/null-style targets, typoing a path that resolves to /dev/..., or saving onto a fifo used by another process. Also happens on systems where a config-managed path (e.g. a ssh control socket) collides with the file being edited.

Related errors


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