jdx/mise · error

package plugin state parent

Error message

package plugin state parent

What it means

`save_state_at` calls `path.parent().expect("package plugin state parent")` before creating directories for the plugin state file. `Path::parent()` returns `None` only for root paths (`/`) or empty paths, so the panic means the plugin's state path has no parent directory — typically because the configured plugin state location is a filesystem root or empty.

Solutions

  1. Configure the plugin state path to a real nested file (e.g. /path/to/state/plugin.json), not a root or bare name
  2. Log/print `state_path()` before saving to see what it resolves to
  3. Change the code to map a missing parent to a descriptive error rather than a panic

Example fix

// before
let path = env_state_dir.unwrap_or_default().join(""); // resolves to ""
// after
let path = env_state_dir.unwrap_or(default_dir).join("plugin.json");
Defensive patterns

Strategy: validation

Validate before calling

let p = state_path();
if p.parent().is_none() {
    return Err(anyhow!("plugin state path {:?} has no parent directory", p));
}

Type guard

fn has_parent(p: &Path) -> bool { p.parent().is_some() }

Prevention

When it happens

Trigger: Calling save_state (src/system/packages/plugin.rs:167) when `state_path()` resolves to `/` or an empty/relative-single-component path with no directory prefix, e.g. a misconfigured state directory like `MISE_..._STATE_PATH=/` or an unset variable producing `""`.

Common situations: Environment variable or config pointing the plugin state file at a mount root; empty path templating in config; running from a chroot with a root-level state path.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/5b3fd8eea6b9e8b0. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/plugin.rs:167

                crate::file::display_path(path)
            );
        }
        if state.manager != manager {
            bail!(
                "package plugin ownership state {} belongs to manager '{}', not '{manager}'",
                crate::file::display_path(path),
                state.manager
            );
        }
        Ok(state)
    }

    fn load_state(&self) -> Result<PackagePluginState> {
        Self::load_state_at(&self.state_path(), &self.name)
    }

    fn save_state_at(path: &std::path::Path, state: &PackagePluginState) -> Result<()> {
        crate::file::create_dir_all(path.parent().expect("package plugin state parent"))?;
        let mut contents = serde_json::to_vec_pretty(state)?;
        contents.push(b'\n');
        crate::file::write_atomic(path, contents)
    }

    fn save_state(&self, state: &PackagePluginState) -> Result<()> {
        Self::save_state_at(&self.state_path(), state)
    }

    pub(crate) fn supports_uninstall(&self) -> bool {
        self.plugin
            .plugin_path
            .join("hooks/package_uninstall.lua")
            .exists()
    }

    fn sync_lookup_path() -> Vec<PathBuf> {
        let mut paths: Vec<PathBuf> = std::env::var_os("PATH")

View on GitHub (pinned to 533346cc37)