jdx/mise · error

invalid Cargo crate name

Error message

invalid Cargo crate name: {name:?}

What it means

get_crate_url builds the sparse crates.io index URL for a crate name, which must be non-empty and ASCII (crate names are ASCII per cargo's rules; the name is lowercased and split into index shards). An empty or non-ASCII name cannot map to a valid index path, so mise bails.

Solutions

  1. Correct the crate name in mise.toml/.tool-versions to the exact ASCII crate name from crates.io.
  2. Remove empty tool entries or unexpanded template variables from the config.
  3. Verify with `mise ls-remote cargo:<name>` once the name is fixed.

Example fix

# before (mise.toml)
[tools]
cargo:"café-cli" = "latest"

# after
[tools]
cargo:"cafe-cli" = "latest"
Defensive patterns

Strategy: validation

Validate before calling

// rust
fn valid_crate_name(name: &str) -> bool {
    !name.is_empty() && name.is_ascii()
}
// call before configuring cargo:<name>
assert!(valid_crate_name("ripgrep"));

Type guard

fn is_ascii_crate_name(name: &str) -> bool {
    !name.is_empty() && name.is_ascii()
}

Prevention

When it happens

Trigger: Resolving the remote version list for a cargo: tool whose crate name is "" or contains non-ASCII characters (called from _list_remote_versions, and exercised by the crate_index_url_rejects_* tests).

Common situations: Typo or empty version specifier in mise.toml such as `cargo:""` or `cargo:@1.0`; pasting a crate name containing a Unicode look-alike character; programmatic/templated tool names where a variable expands to empty.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/backend/cargo.rs:521

        } else if let Some((user, repo)) = self.tool_name().split_once('/') {
            format!("https://github.com/{user}/{repo}.git").parse().ok()
        } else {
            None
        }
    }
}

fn format_tool_options(options: &[&'static str]) -> String {
    options
        .iter()
        .map(|option| format!("`{option}`"))
        .collect::<Vec<_>>()
        .join(", ")
}

fn get_crate_url(name: &str) -> eyre::Result<Url> {
    if name.is_empty() || !name.is_ascii() {
        bail!("invalid Cargo crate name: {name:?}");
    }
    let name = name.to_lowercase();
    let url = match name.len() {
        1 => format!("https://index.crates.io/1/{name}"),
        2 => format!("https://index.crates.io/2/{name}"),
        3 => format!("https://index.crates.io/3/{}/{name}", &name[..1]),
        _ => format!(
            "https://index.crates.io/{}/{}/{name}",
            &name[..2],
            &name[2..4]
        ),
    };
    Ok(url.parse()?)
}

fn parse_crate_versions(response: &str) -> eyre::Result<Vec<VersionInfo>> {
    let mut versions = vec![];
    for version in Deserializer::from_str(response).into_iter::<CrateVersion>() {

View on GitHub (pinned to 533346cc37)