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
- Fix the plugin's hook to return only recognized state strings ('installed', 'missing', or the structured variants documented for the hook contract).
- Update the plugin to the version matching your mise release, or update mise to match the plugin's contract.
- Reproduce the hook manually (run the status hook for the failing package) and inspect the raw `state` value it emits.
- 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
- Write plugin hooks against the documented state vocabulary only ('installed', 'missing', structured variants).
- Add a unit test for your Lua hook that asserts every returned entry's state is in the valid set.
- Keep plugin and mise versions in sync — state strings are part of the hook contract.
- Never hand-edit hook return values without re-checking the contract after mise upgrades.
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
- package hook did not return state for
- package plugin ' ' does not support uninstall; add…
- cloned request builder should remain cloneable
- Consider enabling 'keep_danger_placeholders' option (to…
- dotfiles: cannot enroll encrypted paths inside an active…
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)