jdx/mise · error

package hook returned invalid state ' ' for

Error message

{} package hook returned invalid state '{}' for '{}'

What it means

Package plugins report each package's status through a Lua hook that returns entries with a `state` field from a fixed vocabulary (e.g. 'installed', 'missing', plus structured variants). When the hook returns an entry for a package with a `state` string mise does not recognize, `installed` bails out naming the plugin, the invalid state, and the package. This catches plugin/contract drift early instead of mapping unknown states to a wrong default.

Solutions

  1. Fix the plugin's hook to return only recognized state strings ('installed', 'missing', or the structured variants documented for the hook contract).
  2. Update the plugin to the version matching your mise release, or update mise to match the plugin's contract.
  3. Reproduce the hook manually (run the status hook for the failing package) and inspect the raw `state` value it emits.
  4. File a bug against the plugin if it claims compatibility with your mise version.

Example fix

-- before (package status hook)
return { { name = 'ripgrep', state = 'present' } }

-- after
return { { name = 'ripgrep', state = 'installed', version = '14.1.0' } }
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate hook output states against the known vocabulary before feeding mise
const VALID_STATES = new Set(['installed', 'missing']);
function auditHookOutput(entries) {
  for (const e of entries ?? []) {
    if (typeof e?.name !== 'string' || !VALID_STATES.has(e?.state) && typeof e?.state !== 'object') {
      throw new Error(`Hook returned invalid state ${JSON.stringify(e?.state)} for ${e?.name}`);
    }
  }
}

Type guard

const hasValidState = (e) => e != null && typeof e.name === 'string' && (e.state === 'installed' || e.state === 'missing' || (typeof e.state === 'object' && e.state !== null));

Try / catch

try { await misePkgInstall(plugin, pkgs) } catch (e) { const m = String(e).match(/hook returned invalid state '(.*?)' for '(.*?)'/); if (m) { console.error(`Fix plugin '${m[1]}': state '${m[2]}' — see hook contract docs`); } else throw e; }

Prevention

When it happens

Trigger: Calling `installed` (via prune_plan, apply_prune_plan, install, or upgrade) where the plugin's status hook returns a package entry whose `state` is a non-empty string outside the known set — typically due to a typo or a plugin written against a newer/older hook contract.

Common situations: A hand-edited or community Lua hook returns e.g. 'instaled' or 'unknown'; the plugin was written for a different mise version whose state vocabulary differed; a plugin returns version-incompatible state strings after a mise upgrade.

Related errors


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

Appendix: source

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

            .packages
            .into_iter()
            .map(|pkg| (pkg.name.clone(), pkg))
            .collect();
        pkgs.iter()
            .map(|request| {
                let returned = by_name.get(&request.name);
                let state = match returned {
                    Some(pkg) if pkg.state == "installed" => {
                        let installed = pkg.version.clone().unwrap_or_default();
                        match &request.version {
                            Some(requested) if requested != &installed => {
                                PackageState::VersionMismatch { installed }
                            }
                            _ => PackageState::Installed { version: installed },
                        }
                    }
                    Some(pkg) if pkg.state == "missing" => PackageState::Missing,
                    Some(pkg) => bail!(
                        "{} package hook returned invalid state '{}' for '{}'",
                        self.name,
                        pkg.state,
                        request.name
                    ),
                    None => bail!(
                        "{} package hook did not return state for '{}'",
                        self.name,
                        request.name
                    ),
                };
                Ok(PackageStatus {
                    request: request.clone(),
                    state,
                })
            })
            .collect()
    }

View on GitHub (pinned to 533346cc37)