jdx/mise · error

invalid pinned revision

Error message

invalid pinned revision

What it means

install_at only accepts a full commit SHA: the revision must be exactly 40 (SHA-1) or 64 (SHA-256) ASCII hex characters. Anything else — branch names, tags, short SHAs, refs like HEAD — is rejected with this error before any network or filesystem work.

Solutions

  1. Resolve the full SHA first: `git rev-parse <ref>` on the source repo, then pass the 40- or 64-char result.
  2. If you only have a tag/branch, fetch the repository (Source::fetch) and resolve HEAD, or run `git ls-remote <origin> <ref>`.
  3. Check for stray whitespace or `origin/` prefixes in the revision string.

Example fix

// before
install_at("https://github.com/org/repo.git", "v1.2.0", ...).await?;
// after
let rev = "9fceb02d0ae598e95dc970b74767f19372d61af8"; // git ls-remote origin v1.2.0
install_at("https://github.com/org/repo.git", rev, ...).await?;
Defensive patterns

Strategy: validation

Validate before calling

function isFullSha(rev: string): boolean {
  return (rev.length === 40 || rev.length === 64) && /^[0-9a-fA-F]+$/.test(rev);
}
if (!isFullSha(revision)) throw new Error("resolve ref to full SHA first: git rev-parse <ref>");

Type guard

const isSha = (s: string): s is string => (s.length === 40 || s.length === 64) && /^[0-9a-f]{40}$|^[0-9a-f]{64}$/.test(s);

Prevention

When it happens

Trigger: Calling install / install_at / install_source / preview_source with revision values like `main`, `v1.2.3`, `abc123` (short SHA), or a SHA containing non-hex characters.

Common situations: Users passing a tag where a commit SHA is required; truncating a SHA when copying from CI logs; using a branch name assuming refs are supported.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at src/system/remote_repository.rs:191

        update,
        yes,
        dry_run,
        &global_directory(),
    )
}

fn install_at(
    bundle: &Path,
    origin: &str,
    revision: &str,
    update: bool,
    yes: bool,
    dry_run: bool,
    destination: &Path,
) -> Result<PathBuf> {
    validate_origin(origin)?;
    if !matches!(revision.len(), 40 | 64) || !revision.bytes().all(|b| b.is_ascii_hexdigit()) {
        bail!("invalid pinned revision");
    }
    if destination.is_symlink() {
        bail!("global configuration directory must not be a symlink");
    }
    let parent = destination
        .parent()
        .ok_or_else(|| eyre::eyre!("missing parent directory"))?;
    if !dry_run {
        std::fs::create_dir_all(parent)?;
    }
    let _lock = crate::lock_file::LockFile::new(destination).lock()?;
    // the checkout is renamed into place, so it is staged next to the
    // destination; a dry run never renames and leaves the parent alone
    let temporary = if dry_run {
        tempfile::tempdir()?
    } else {
        tempfile::tempdir_in(parent)?
    };

View on GitHub (pinned to 533346cc37)