jdx/mise · error

has a wheel without a SHA256 hash

Error message

{name} has a wheel without a SHA256 hash

What it means

Every wheel recorded in the uv lock must carry a verifiable SHA256 integrity hash. validate_uv_lock extracts each wheel's `hash`, strips the `sha256:` prefix, and requires a 64-character hex digest; anything else (missing hash, wrong prefix, wrong length or non-hex characters) is rejected so installs cannot pull tampered or unverifiable artifacts.

Solutions

  1. Regenerate the lock with hashes: `mise lock --bump <tool>` using a current uv (>= 0.12.10).
  2. If hand-editing, ensure each wheel has `hash = "sha256:<64-hex>"` matching the artifact digest.
  3. Point uv at an index that serves per-file hashes (e.g. official PyPI) and re-lock.
  4. Check for tooling (formatting, filtering scripts) that stripped the hash fields from the lock.

Example fix

// before
[[package.wheels]]
url = "https://files.pythonhosted.org/.../pkg-1.0-py3-none-any.whl"

// after
[[package.wheels]]
url = "https://files.pythonhosted.org/.../pkg-1.0-py3-none-any.whl"
hash = "sha256:0123abcd..."  # 64 hex chars
Defensive patterns

Strategy: validation

Validate before calling

// verify every wheel hash before trusting a lock
for w in lock.wheels: assert re.fullmatch(r'[0-9a-f]{64}', w.hash.removeprefix('sha256:'));

Prevention

When it happens

Trigger: Thrown from validate_uv_lock when a wheel entry in the lock graph has no `hash` key, a hash lacking the `sha256:` prefix, or a malformed digest (not 64 ASCII hex characters). Calls come from prepare_install_version, resolve_uv_lock, install_uv_lock.

Common situations: A hand-edited or truncated mise.lock; a lock generated with hash checking disabled or by tooling that strips hashes; a private index that omits hashes; an old uv version that emitted different hash formats.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/backend/pipx/lock.rs:416

                && package.get("version").and_then(toml::Value::as_str) == Some(&tv.version)
            {
                root = true;
            }
            let wheels = package
                .get("wheels")
                .and_then(toml::Value::as_array)
                .filter(|v| !v.is_empty())
                .ok_or_else(|| {
                    eyre!("{name} has no published wheels; Python graph locks require wheels")
                })?;
            for wheel in wheels {
                let hash = wheel
                    .get("hash")
                    .and_then(toml::Value::as_str)
                    .and_then(|h| h.strip_prefix("sha256:"));
                if !hash.is_some_and(|h| h.len() == 64 && h.bytes().all(|c| c.is_ascii_hexdigit()))
                {
                    bail!("{name} has a wheel without a SHA256 hash");
                }
            }
        }
        if !root || !virtual_root {
            bail!("Python lock is missing the requested root package");
        }
        validate_portable_urls(&toml::Value::Table(lock.graph.clone()))?;
        // No arbitrary project settings or build systems are accepted from a lockfile.
        if lock.project.len() != 1 || project.len() != 4 {
            bail!("unsupported Python lock project settings");
        }
        Ok(())
    }

    pub(super) async fn install_uv_lock(
        &self,
        ctx: &InstallContext,
        tv: &ToolVersion,

View on GitHub (pinned to 533346cc37)