jdx/mise · error

package hook did not return state for

Error message

{} package hook did not return state for '{}'

What it means

When mise asks a package plugin for a package's status, the hook is expected to return a state entry for every requested package. If the returned list contains no entry for a requested package (`None` case in the match), `installed` fails with the plugin name and package name rather than inventing a default status. This guarantees status checks are total: every request gets an explicit answer from the hook.

Solutions

  1. Fix the plugin's status hook to return an entry (installed or missing) for every requested package name.
  2. Check for name mismatches between the request and the hook's reported names (aliases, case, versioned names) and normalize them in the hook.
  3. Run the plugin's status hook manually with the failing package name to see why it is omitted.
  4. Update the plugin to a version that handles unknown/missing packages by reporting state 'missing' instead of omitting them.

Example fix

-- before: hook omits untracked packages
local results = {}
for _, p in ipairs(tracked) do table.insert(results, p) end
return results

-- after: always answer for every requested package
for _, req in ipairs(requested) do
  if not find(results, req.name) then
    table.insert(results, { name = req.name, state = 'missing' })
  end
end
return results
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the hook answers for every requested package before calling mise
function assertTotal(requested, returned) {
  const got = new Set((returned ?? []).map((e) => e?.name));
  const missing = requested.filter((r) => !got.has(r.name));
  if (missing.length) throw new Error(`Hook omitted state for: ${missing.map((m) => m.name).join(', ')}`);
}

Type guard

const answersAll = (requested, returned) => { const got = new Set((returned ?? []).map((e) => e?.name)); return requested.every((r) => got.has(r.name)); };

Try / catch

try { await misePkgInstall(plugin, pkgs) } catch (e) { const m = String(e).match(/hook did not return state for '(.*?)'/); if (m) { console.error(`Plugin hook omitted '${m[1]}' — normalize names or default to state 'missing'`); } 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 list that omits the requested package entirely — e.g. the hook filters out packages it does not recognize or its query silently skips failures.

Common situations: A plugin hook that only reports packages present in its own config, while mise asks about a package added elsewhere; a Lua hook bug where an error for one package causes it to be dropped from the result; name normalization differences (case/alias) between mise's request and the hook's keys.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

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

                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()
    }

    async fn install(&self, pkgs: &[PackageRequest], opts: &InstallOpts) -> Result<()> {
        if opts.dry_run {
            return self.action(pkgs, opts, false).await;
        }
        let _lock = self.operation_lock()?;

View on GitHub (pinned to 533346cc37)