jdx/mise · error

refusing to replace non-file path {}; set replace = true to

Error message

refusing to replace non-file path {}; set replace = true to allow replacement

What it means

Raised in ManagedFileRequest::operation when a file entry (any state) is planned and inspection returns ResourceAction::Unknown because the path exists as something other than a regular file (directory, socket, etc.), and the entry does not opt in with replace = true. The library refuses to clobber non-file filesystem objects silently.

Source

Thrown at src/system/managed_files.rs:600

            replace: config.replace,
            notify: config.notify,
            origin,
            inspection: None,
        })
    }

    pub(crate) fn plan(&self) -> Result<ResourcePlan> {
        plan_file(self).map(|plan| plan.with_origin(self.origin.clone()))
    }

    fn operation(&self) -> Result<Option<PrivilegedAction>> {
        match self.plan()?.action {
            ResourceAction::Noop => return Ok(None),
            ResourceAction::Unknown if self.state == ManagedState::Absent => bail!(
                "refusing to remove directory {} as a file; declare it in [bootstrap.directories]",
                self.path.display()
            ),
            ResourceAction::Unknown => bail!(
                "refusing to replace non-file path {}; set replace = true to allow replacement",
                self.path.display()
            ),
            _ => {}
        }
        Ok(Some(match self.state {
            ManagedState::Present => PrivilegedAction::WriteFile {
                path: self.path.clone(),
                content: self.content.clone().expect("present file has content"),
                owner: self.owner.clone(),
                group: self.group.clone(),
                mode: self.mode,
                replace: self.replace,
            },
            ManagedState::Absent => PrivilegedAction::RemoveFile {
                path: self.path.clone(),
            },
        }))

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Set replace = true on the file entry to explicitly allow the library to remove and replace the non-file path.
  2. Verify the path is expected to be a non-file; if it should stay a directory, manage it via [bootstrap.directories] instead.
  3. Choose a different target path that does not collide with the daemon-created object.
  4. Stop whatever process recreates the object at that path before converging.

Example fix

# before
[[bootstrap.files]]
path = "/var/run/myapp/sock.conf"
content = "..."
state = "present"

# after
[[bootstrap.files]]
path = "/var/run/myapp/sock.conf"
content = "..."
state = "present"
replace = true
Defensive patterns

Strategy: validation

Validate before calling

for path in &managed_file_paths {
    if let Ok(meta) = std::fs::symlink_metadata(path) {
        if !meta.is_file() && !replace_enabled(path) {
            eprintln!("{} is not a regular file; set replace = true or fix the entry", path.display());
        }
    }
}

Type guard

fn is_regular_file(path: &std::path::Path) -> bool {
    std::fs::metadata(path).map(|m| m.is_file()).unwrap_or(true)
}

Prevention

When it happens

Trigger: Converging a [bootstrap.files] entry (state = "present" or "absent"; for absent-and-directory see the sibling error) whose path exists as a non-regular-file and `replace` is false/unset — the (ResourceAction::Unknown, non-Absent) arm.

Common situations: A service created a directory or socket at the path the config wants to own; a symlink was replaced by a real directory; managing a path like /var/run/myapp that a daemon recreates as a directory on every start.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/6991963a7e0213ba. Report an issue: GitHub.